1 /*
2 * FreeRTOS+TCP V2.3.1
3 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * http://aws.amazon.com/freertos
23 * http://www.FreeRTOS.org
24 */
25
26 /**
27 * @file FreeRTOS_DHCPv6.c
28 * @brief A DHCPv6 client.
29 */
30
31 /* Standard includes. */
32 #include <stdio.h>
33 #include <ctype.h>
34
35 /* FreeRTOS includes. */
36 #include <FreeRTOS.h>
37 #include "task.h"
38 #include "timers.h"
39 #include "queue.h"
40 #include "semphr.h"
41
42 /* FreeRTOS+TCP includes. */
43 #include "FreeRTOS_IP.h"
44
45 /* *INDENT-OFF* */
46 #if ( ipconfigUSE_IPv6 != 0 ) && ( ipconfigUSE_DHCPv6 != 0 )
47 /* *INDENT-ON* */
48
49 #include "FreeRTOS_Sockets.h"
50 #include "FreeRTOS_DHCPv6.h"
51 #include "FreeRTOS_DNS.h"
52 #include "NetworkBufferManagement.h"
53 #include "FreeRTOS_ARP.h"
54 #include "FreeRTOS_Sockets.h"
55 #include "FreeRTOS_IP_Private.h"
56 #include "FreeRTOS_IP_Timers.h"
57
58 #include "FreeRTOS_BitConfig.h"
59
60 #include "FreeRTOS_Routing.h"
61
62 #include "FreeRTOS_ND.h"
63
64 /* Timer parameters */
65 #ifndef dhcpINITIAL_DHCP_TX_PERIOD
66 /** @brief DHCP timer period in ms */
67 #define dhcpINITIAL_TIMER_PERIOD ( pdMS_TO_TICKS( 250U ) )
68 /** @brief DHCP transmit period in ms */
69 #define dhcpINITIAL_DHCP_TX_PERIOD ( pdMS_TO_TICKS( 5000U ) )
70 #endif
71
72 /** @brief The following define is temporary and serves to make the /single source
73 * code more similar to the /multi version. */
74
75 #define EP_DHCPData pxEndPoint->xDHCPData
76 /** @brief Macro to access the IPv6 settings from the pxEndPoint */
77 #define EP_IPv6_SETTINGS pxEndPoint->ipv6_settings
78
79 /** @brief The maximum size of send buffer. */
80 #define DHCPv6_SEND_MAX_BUFFER_SIZE ( 256 )
81
82 /** @brief When a reply is received, some options are mandatory for this driver. */
83 #define dhcpMANDATORY_OPTIONS \
84 ( ( ( ( uint32_t ) 1U ) << DHCPv6_Option_Client_Identifier ) | \
85 ( ( ( uint32_t ) 1U ) << DHCPv6_Option_Server_Identifier ) )
86
87 /** @brief The UDP socket which is shared by all end-points that need DHCPv6. */
88 static Socket_t xDHCPv6Socket;
89
90 /** @brief A reference count makes sure that the UDP socket will be deleted when it
91 * is not used anymore. */
92 static BaseType_t xDHCPv6SocketUserCount;
93
94 static BaseType_t prvIsOptionLengthValid( uint16_t usOption,
95 size_t uxOptionLength,
96 size_t uxRemainingSize );
97
98 static BaseType_t prvDHCPv6Analyse( struct xNetworkEndPoint * pxEndPoint,
99 const uint8_t * pucAnswer,
100 size_t uxTotalLength,
101 DHCPMessage_IPv6_t * pxDHCPMessage );
102
103 static void vDHCPv6ProcessEndPoint( BaseType_t xReset,
104 NetworkEndPoint_t * pxEndPoint,
105 DHCPMessage_IPv6_t * pxDHCPMessage );
106
107 static void prvInitialiseDHCPv6( NetworkEndPoint_t * pxEndPoint );
108
109 static void prvSendDHCPMessage( NetworkEndPoint_t * pxEndPoint );
110
111 /*
112 * Create the DHCP socket, if it has not been created already.
113 */
114 static void prvCreateDHCPv6Socket( NetworkEndPoint_t * pxEndPoint );
115
116 /*
117 * Close the DHCP socket, only when not in use anymore (i.e. xDHCPv6SocketUserCount = 0).
118 */
119 static void prvCloseDHCPv6Socket( NetworkEndPoint_t * pxEndPoint );
120
121 #if ( ipconfigHAS_DEBUG_PRINTF == 1 )
122 static const char * prvStateName( eDHCPState_t eState );
123 #endif
124
125 static BaseType_t xDHCPv6Process_PassReplyToEndPoint( struct xNetworkEndPoint * pxEndPoint );
126
127 static void vDHCPv6ProcessEndPoint_HandleReply( NetworkEndPoint_t * pxEndPoint,
128 DHCPMessage_IPv6_t * pxDHCPMessage );
129
130
131 static BaseType_t xDHCPv6ProcessEndPoint_HandleAdvertise( NetworkEndPoint_t * pxEndPoint,
132 DHCPMessage_IPv6_t * pxDHCPMessage );
133
134 static BaseType_t xDHCPv6ProcessEndPoint_HandleState( NetworkEndPoint_t * pxEndPoint,
135 DHCPMessage_IPv6_t * pxDHCPMessage );
136
137 static BaseType_t prvDHCPv6_subOption( uint16_t usOption,
138 const DHCPOptionSet_t * pxSet,
139 DHCPMessage_IPv6_t * pxDHCPMessage,
140 BitConfig_t * pxMessage );
141
142 static BaseType_t prvDHCPv6_handleOption( struct xNetworkEndPoint * pxEndPoint,
143 uint16_t usOption,
144 const DHCPOptionSet_t * pxSet,
145 DHCPMessage_IPv6_t * pxDHCPMessage,
146 BitConfig_t * pxMessage );
147
148
149 /*-----------------------------------------------------------*/
150
151 /**
152 * @brief DHCP IPv6 message object
153 */
154 static DHCPMessage_IPv6_t xDHCPMessage;
155
156 /**
157 * @brief Get the DHCP state from a given endpoint.
158 *
159 * @param[in] pxEndPoint The end-point for which vDHCPv6Process() is called.
160 *
161 * @return DHCP state of the given endpoint
162 *
163 */
eGetDHCPv6State(struct xNetworkEndPoint * pxEndPoint)164 eDHCPState_t eGetDHCPv6State( struct xNetworkEndPoint * pxEndPoint )
165 {
166 configASSERT( pxEndPoint );
167 return EP_DHCPData.eDHCPState;
168 }
169 /*-----------------------------------------------------------*/
170
171 /**
172 * @brief Check if option length is less than buffer size and larger than minimum requirement.
173 *
174 * @param[in] usOption The option code.
175 * @param[in] uxOptionLength The option length to check.
176 * @param[in] uxRemainingSize Remaining size in the buffer.
177 *
178 * @return pdTRUE if the length is valid, otherwise pdFALSE.
179 */
prvIsOptionLengthValid(uint16_t usOption,size_t uxOptionLength,size_t uxRemainingSize)180 static BaseType_t prvIsOptionLengthValid( uint16_t usOption,
181 size_t uxOptionLength,
182 size_t uxRemainingSize )
183 {
184 BaseType_t xReturn = pdTRUE;
185 size_t uxMinOptLength = 0U;
186
187 switch( usOption )
188 {
189 case DHCPv6_Option_NonTemporaryAddress:
190 /* Refer to RFC3315 - sec 22.4, the length of IA_NA should never less than 12. */
191 uxMinOptLength = 12U;
192 break;
193
194 case DHCPv6_Option_TemporaryAddress:
195 /* Refer to RFC3315 - sec 22.5, the length of IA_TA should never less than 4. */
196 uxMinOptLength = 4U;
197 break;
198
199 case DHCPv6_Option_IA_Address:
200 /* Refer to RFC3315 - sec 22.6, the length of IA should never less than 24. */
201 uxMinOptLength = 24U;
202 break;
203
204 case DHCPv6_Option_Preference:
205 /* Refer to RFC3315 - sec 22.8, the length of IA should never less than 1. */
206 uxMinOptLength = 1U;
207 break;
208
209 case DHCPv6_Option_Status_Code:
210 /* Refer to RFC3315 - sec 22.13, the length of status code should never less than 2. */
211 uxMinOptLength = 2U;
212 break;
213
214 case DHCPv6_Option_IA_for_Prefix_Delegation:
215 /* Refer to RFC3633 - sec 9, the length of IA_PD should never less than 12. */
216 uxMinOptLength = 12U;
217 break;
218
219 case DHCPv6_Option_IA_Prefix:
220 /* Refer to RFC3633 - sec 10, the length of status code should never less than 25. */
221 uxMinOptLength = 25U;
222 break;
223
224 default:
225 /* No constrain for other options. */
226 uxMinOptLength = 0U;
227 break;
228 }
229
230 if( uxOptionLength < uxMinOptLength )
231 {
232 FreeRTOS_printf( ( "prvIsOptionLengthValid: Length %lu of option %u is less than minimum requirement %lu\n",
233 uxOptionLength,
234 usOption,
235 uxMinOptLength ) );
236 xReturn = pdFALSE;
237 }
238 else if( uxOptionLength > uxRemainingSize )
239 {
240 FreeRTOS_printf( ( "prvIsOptionLengthValid: Length %lu of option %u is larger than remaining buffer size %lu\n",
241 uxOptionLength,
242 usOption,
243 uxRemainingSize ) );
244 xReturn = pdFALSE;
245 }
246 else
247 {
248 /* Do nothing. */
249 }
250
251 return xReturn;
252 }
253
254 /**
255 * @brief A DHCP packet has a list of options, one of them is Status Code. This function is used to parse it.
256 * @param[in] uxLength Total length for status code.
257 * @param[in] pxMessage The raw packet as it was received.
258 *
259 * @return pdTRUE if status is success, otherwise pdFALSE.
260 */
prvDHCPv6_handleStatusCode(size_t uxLength,BitConfig_t * pxMessage)261 static BaseType_t prvDHCPv6_handleStatusCode( size_t uxLength,
262 BitConfig_t * pxMessage )
263 {
264 BaseType_t xReturn = pdTRUE;
265 /* Since length is checked before entering, we can read message directly. */
266 uint16_t usStatus = usBitConfig_read_16( pxMessage );
267 uint8_t ucMessage[ 50 ];
268 /* Minus 2 because we read 2 bytes for usStatus. */
269 size_t uxReadLength = uxLength - 2U;
270
271 FreeRTOS_printf( ( "Got status code %u\n",
272 usStatus ) );
273
274 if( uxReadLength > sizeof( ucMessage ) - 1U )
275 {
276 uxReadLength = sizeof( ucMessage ) - 1U;
277 }
278
279 ( void ) xBitConfig_read_uc( pxMessage, ucMessage, uxReadLength );
280 ucMessage[ uxReadLength ] = 0;
281 FreeRTOS_printf( ( "Msg: '%s'\n", ucMessage ) );
282
283 /* Read the remainder, if present. */
284 if( uxLength > uxReadLength + 2U )
285 {
286 uxReadLength = uxLength - ( uxReadLength + 2U );
287 ( void ) xBitConfig_read_uc( pxMessage, NULL, uxReadLength );
288 }
289
290 /* A status of 0 means: Success. (RFC 3315 - sec 24.4). */
291 if( usStatus != 0U )
292 {
293 xReturn = pdFALSE;
294 }
295
296 return xReturn;
297 }
298
299 /**
300 * @brief A DHCPv6 reply has been received. See to which end-point it belongs and pass it.
301 *
302 * @param[in] pxEndPoint The end-point for which vDHCPv6Process() is called.
303 *
304 * @return In case the message is passed to 'pxEndPoint', return pdFALSE, meaning that
305 * the it has done its periodic processing.
306 */
xDHCPv6Process_PassReplyToEndPoint(struct xNetworkEndPoint * pxEndPoint)307 static BaseType_t xDHCPv6Process_PassReplyToEndPoint( struct xNetworkEndPoint * pxEndPoint )
308 {
309 uint32_t ulCompareResult = pdTRUE;
310 BaseType_t xDoProcess = pdTRUE;
311 struct xNetworkEndPoint * pxIterator;
312
313 pxIterator = pxNetworkEndPoints;
314
315 /* Find the end-point with given transaction ID. */
316 while( pxIterator != NULL )
317 {
318 if( ( pxIterator->bits.bIPv6 != pdFALSE_UNSIGNED ) && ( pxIterator->bits.bWantDHCP != pdFALSE_UNSIGNED ) )
319 {
320 FreeRTOS_printf( ( "vDHCPProcess: 0x%06X == 0x%06X ?\n",
321 ( unsigned int ) xDHCPMessage.ulTransactionID,
322 ( unsigned int ) pxIterator->xDHCPData.ulTransactionId ) );
323
324 if( ( xDHCPMessage.ulTransactionID == pxIterator->xDHCPData.ulTransactionId ) &&
325 ( pxIterator->xDHCPData.eDHCPState != eLeasedAddress ) )
326 {
327 break;
328 }
329 }
330
331 pxIterator = pxIterator->pxNext;
332 }
333
334 if( pxIterator != NULL )
335 {
336 if( pxIterator->pxDHCPMessage->xServerID.usDUIDType != 0U )
337 {
338 /* Check if the ID-type, the length and the contents are equal. */
339 if( pxIterator->pxDHCPMessage->xServerID.uxLength > DHCPv6_MAX_CLIENT_SERVER_ID_LENGTH )
340 {
341 FreeRTOS_printf( ( "DHCPv6 invalid uxLength.\n" ) );
342 ulCompareResult = pdFAIL;
343 }
344 else if( ( xDHCPMessage.xServerID.usDUIDType != pxIterator->pxDHCPMessage->xServerID.usDUIDType ) ||
345 ( xDHCPMessage.xServerID.uxLength != pxIterator->pxDHCPMessage->xServerID.uxLength ) ||
346 ( memcmp( xDHCPMessage.xServerID.pucID, pxIterator->pxDHCPMessage->xServerID.pucID, pxIterator->pxDHCPMessage->xServerID.uxLength ) != 0 ) )
347 {
348 FreeRTOS_printf( ( "DHCPv6 reply contains an unknown ID.\n" ) );
349 ulCompareResult = pdFAIL;
350 }
351 else
352 {
353 /* do nothing, coverity happy */
354 }
355 }
356
357 if( ulCompareResult == pdPASS )
358 {
359 ( void ) memcpy( ( void * ) pxIterator->pxDHCPMessage, ( const void * ) &xDHCPMessage, sizeof( xDHCPMessage ) );
360
361 /* The second parameter pdTRUE tells to check for a UDP message. */
362 vDHCPv6ProcessEndPoint( pdFALSE, pxIterator, pxIterator->pxDHCPMessage );
363 pxIterator->pxDHCPMessage->ucHasUID = 0U;
364
365 if( pxEndPoint == pxIterator )
366 {
367 xDoProcess = pdFALSE;
368 }
369 }
370 }
371
372 return xDoProcess;
373 }
374 /*-----------------------------------------------------------*/
375
376 /**
377 * @brief Check the DHCP socket and run one cycle of the DHCP state machine.
378 *
379 * @param[in] xReset When pdTRUE, the state machine needs to be reset. This may happen
380 * when the end-point has just become up.
381 * @param[in] pxEndPoint The end-point that wants a DHCPv6 address.
382 */
vDHCPv6Process(BaseType_t xReset,struct xNetworkEndPoint * pxEndPoint)383 void vDHCPv6Process( BaseType_t xReset,
384 struct xNetworkEndPoint * pxEndPoint )
385 {
386 BaseType_t xDoProcess = pdTRUE;
387
388 configASSERT( pxEndPoint != NULL );
389
390 /* Is DHCP starting over? */
391 if( xReset != pdFALSE )
392 {
393 EP_DHCPData.eDHCPState = eInitialWait;
394
395 if( pxEndPoint->pxDHCPMessage == NULL )
396 {
397 pxEndPoint->pxDHCPMessage = pvPortMalloc( sizeof( *pxEndPoint->pxDHCPMessage ) );
398
399 if( pxEndPoint->pxDHCPMessage != NULL )
400 {
401 ( void ) memset( pxEndPoint->pxDHCPMessage, 0, sizeof( *pxEndPoint->pxDHCPMessage ) );
402 }
403 else
404 {
405 FreeRTOS_printf( ( "vDHCPv6Process: malloc failed %u bytes\n", ( unsigned int ) sizeof( *pxEndPoint->pxDHCPMessage ) ) );
406
407 /* Use static IP address. */
408 taskENTER_CRITICAL();
409 {
410 ( void ) memcpy( EP_IPv6_SETTINGS.xIPAddress.ucBytes, pxEndPoint->ipv6_defaults.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
411 iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IPv6_ADDRESS( EP_IPv6_SETTINGS.xIPAddress );
412 }
413 taskEXIT_CRITICAL();
414
415 EP_DHCPData.eDHCPState = eNotUsingLeasedAddress;
416 vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE );
417 xDoProcess = pdFALSE;
418 }
419 }
420 }
421
422 /* If there is a socket, check for incoming messages first. */
423 if( ( xDoProcess != pdFALSE ) && ( EP_DHCPData.xDHCPSocket != NULL ) )
424 {
425 uint8_t * pucUDPPayload;
426
427 BaseType_t lBytes;
428 size_t uxLength;
429
430 for( ; ; )
431 {
432 BaseType_t xResult;
433 BaseType_t xRecvFlags = ( BaseType_t ) FREERTOS_ZERO_COPY;
434
435 /* Get the next UDP message. */
436 lBytes = FreeRTOS_recvfrom( EP_DHCPData.xDHCPSocket, &( pucUDPPayload ), 0, xRecvFlags, NULL, NULL );
437
438 if( lBytes <= 0 )
439 {
440 if( ( lBytes < 0 ) && ( lBytes != -pdFREERTOS_ERRNO_EAGAIN ) )
441 {
442 FreeRTOS_printf( ( "vDHCPProcess: FreeRTOS_recvfrom returns %d\n", ( int ) lBytes ) );
443 }
444
445 break;
446 }
447
448 uxLength = ( size_t ) lBytes;
449
450 xResult = prvDHCPv6Analyse( pxEndPoint, pucUDPPayload, uxLength, &( xDHCPMessage ) );
451
452 FreeRTOS_printf( ( "prvDHCPv6Analyse: %s\n", ( xResult == pdPASS ) ? "Pass" : "Fail" ) );
453
454 if( xResult == pdPASS )
455 {
456 xDoProcess = xDHCPv6Process_PassReplyToEndPoint( pxEndPoint );
457 }
458 }
459 }
460
461 if( xDoProcess != pdFALSE )
462 {
463 /* Process the end-point, but do not expect incoming packets. */
464 vDHCPv6ProcessEndPoint( xReset, pxEndPoint, pxEndPoint->pxDHCPMessage );
465 }
466 }
467 /*-----------------------------------------------------------*/
468
469 /**
470 * @brief The DHCP process is about ready: the server sends a confirmation that the
471 * assigned IPv6 address may be used. The settings will be copied to 'pxEndPoint->ipv6_settings'.
472 * @param[in] pxEndPoint The end-point that is asking for an IP-address.
473 * @param[in] pxDHCPMessage The reply received from the DHCP server.
474 */
vDHCPv6ProcessEndPoint_HandleReply(NetworkEndPoint_t * pxEndPoint,DHCPMessage_IPv6_t * pxDHCPMessage)475 static void vDHCPv6ProcessEndPoint_HandleReply( NetworkEndPoint_t * pxEndPoint,
476 DHCPMessage_IPv6_t * pxDHCPMessage )
477 {
478 size_t uxDNSIndex;
479
480 FreeRTOS_printf( ( "vDHCPProcess: acked %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) );
481
482 /* DHCP completed. The IP address can now be used, and the
483 * timer set to the lease timeout time. */
484 pxEndPoint->ipv6_settings.uxPrefixLength = pxDHCPMessage->ucprefixLength; /* Number of valid bytes in the network prefix. */
485 ( void ) memcpy( pxEndPoint->ipv6_settings.xIPAddress.ucBytes, pxDHCPMessage->xIPAddress.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
486 ( void ) memcpy( pxEndPoint->ipv6_settings.xPrefix.ucBytes, pxDHCPMessage->xPrefixAddress.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS ); /* The network prefix, e.g. fe80::/10 */
487 /*pxEndPoint->xGatewayAddress; / * Gateway to the web. * / */
488
489 for( uxDNSIndex = 0; uxDNSIndex < pxDHCPMessage->uxDNSCount; uxDNSIndex++ )
490 {
491 ( void ) memcpy( pxEndPoint->ipv6_settings.xDNSServerAddresses[ uxDNSIndex ].ucBytes, pxDHCPMessage->xDNSServers[ uxDNSIndex ].xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
492 }
493
494 EP_DHCPData.eDHCPState = eLeasedAddress;
495
496 iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress );
497
498 /* Close socket to ensure packets don't queue on it. */
499 prvCloseDHCPv6Socket( pxEndPoint );
500
501 if( EP_DHCPData.ulLeaseTime == 0U )
502 {
503 EP_DHCPData.ulLeaseTime = dhcpv6DEFAULT_LEASE_TIME;
504 }
505 else if( EP_DHCPData.ulLeaseTime < dhcpv6MINIMUM_LEASE_TIME )
506 {
507 EP_DHCPData.ulLeaseTime = dhcpv6MINIMUM_LEASE_TIME;
508 }
509 else
510 {
511 /* The lease time is already valid. */
512 }
513
514 /* Check for clashes. */
515
516 vDHCP_RATimerReload( ( struct xNetworkEndPoint * ) pxEndPoint, EP_DHCPData.ulLeaseTime );
517
518 /* DHCP failed, the default configured IP-address will be used
519 * Now call vIPNetworkUpCalls() to send the network-up event and
520 * start the ARP timer. */
521 vIPNetworkUpCalls( pxEndPoint );
522 }
523 /*-----------------------------------------------------------*/
524
525 /**
526 * @brief An advertise packet has been received. Ask the application if
527 * it it shall send a request to obtain this IP-address.
528 * @param[in] pxEndPoint The end-point that is asking for an IP-address.
529 * @param[in] pxDHCPMessage The advertisement received from the DHCP server.
530 * @return When the request will be send, pdFALSE will be returned.
531 */
xDHCPv6ProcessEndPoint_HandleAdvertise(NetworkEndPoint_t * pxEndPoint,DHCPMessage_IPv6_t * pxDHCPMessage)532 static BaseType_t xDHCPv6ProcessEndPoint_HandleAdvertise( NetworkEndPoint_t * pxEndPoint,
533 DHCPMessage_IPv6_t * pxDHCPMessage )
534 {
535 BaseType_t xGivingUp = pdFALSE;
536
537 #if ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 )
538 eDHCPCallbackAnswer_t eAnswer;
539 #endif /* ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 ) */
540
541 #if ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 )
542 /* Ask the user if a DHCP request is required. */
543 eAnswer = xApplicationDHCPHook_Multi( eDHCPPhasePreRequest, pxEndPoint, &( pxDHCPMessage->xIPAddress ) );
544
545 if( eAnswer == eDHCPContinue )
546 #endif /* ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 ) */
547 {
548 /* An offer has been made, the user wants to continue,
549 * generate the request. */
550 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
551 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
552 /* Force creating a new transaction ID. */
553 pxDHCPMessage->ucHasUID = 0U;
554 prvSendDHCPMessage( pxEndPoint );
555 EP_DHCPData.eDHCPState = eWaitingAcknowledge;
556 }
557
558 #if ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 )
559 else
560 {
561 if( eAnswer == eDHCPUseDefaults )
562 {
563 ( void ) memcpy( &( pxEndPoint->ipv6_settings ), &( pxEndPoint->ipv6_defaults ), sizeof( pxEndPoint->ipv6_settings ) );
564 }
565
566 /* The user indicates that the DHCP process does not continue. */
567 FreeRTOS_debug_printf( ( "xGivingUp because call-back 2\n" ) );
568 xGivingUp = pdTRUE;
569 }
570 #endif /* ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 ) */
571
572 return xGivingUp;
573 }
574 /*-----------------------------------------------------------*/
575
576 /**
577 * @brief This function is called periodically, or when a message was received for this end-point.
578 * @param[in] pxEndPoint The end-point that is asking for an IP-address.
579 * @param[in] pxDHCPMessage when not NULL, a message that was received for this end-point.
580 * @return It returns pdTRUE in case the DHCP process is to be cancelled.
581 */
xDHCPv6ProcessEndPoint_HandleState(NetworkEndPoint_t * pxEndPoint,DHCPMessage_IPv6_t * pxDHCPMessage)582 static BaseType_t xDHCPv6ProcessEndPoint_HandleState( NetworkEndPoint_t * pxEndPoint,
583 DHCPMessage_IPv6_t * pxDHCPMessage )
584
585 {
586 BaseType_t xGivingUp = pdFALSE;
587
588 #if ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 )
589 eDHCPCallbackAnswer_t eAnswer;
590 #endif /* ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 ) */
591
592 configASSERT( pxDHCPMessage != NULL );
593
594 switch( EP_DHCPData.eDHCPState )
595 {
596 case eInitialWait:
597
598 /* Initial state. Create the DHCP socket, timer, etc. if they
599 * have not already been created. */
600 prvInitialiseDHCPv6( pxEndPoint );
601 EP_DHCPData.eDHCPState = eWaitingSendFirstDiscover;
602 break;
603
604 case eWaitingSendFirstDiscover:
605 /* Ask the user if a DHCP discovery is required. */
606 #if ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 )
607 eAnswer = xApplicationDHCPHook_Multi( eDHCPPhasePreDiscover, pxEndPoint, &( pxDHCPMessage->xIPAddress ) );
608
609 if( eAnswer == eDHCPContinue )
610 #endif /* ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 ) */
611 {
612 /* See if prvInitialiseDHCPv6() has created a socket. */
613 if( EP_DHCPData.xDHCPSocket == NULL )
614 {
615 FreeRTOS_debug_printf( ( "xGivingUp because socket is closed\n" ) );
616 xGivingUp = pdTRUE;
617 }
618 else
619 {
620 /* Send the first discover request. */
621 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
622 prvSendDHCPMessage( pxEndPoint );
623 EP_DHCPData.eDHCPState = eWaitingOffer;
624 }
625 }
626
627 #if ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 )
628 else
629 {
630 if( eAnswer == eDHCPUseDefaults )
631 {
632 ( void ) memcpy( &( pxEndPoint->ipv6_settings ), &( pxEndPoint->ipv6_defaults ), sizeof( pxEndPoint->ipv6_settings ) );
633 }
634
635 /* The user indicates that the DHCP process does not continue. */
636 FreeRTOS_debug_printf( ( "xGivingUp because call-back\n" ) );
637 xGivingUp = pdTRUE;
638 }
639 #endif /* ( ipconfigUSE_DHCP_HOOK != 0 ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 ) */
640 break;
641
642 case eWaitingOffer:
643
644 xGivingUp = pdFALSE;
645
646 /* Look for offers coming in. */
647 if( pxDHCPMessage->uxMessageType == DHCPv6_message_Type_Advertise )
648 {
649 xGivingUp = xDHCPv6ProcessEndPoint_HandleAdvertise( pxEndPoint, pxDHCPMessage );
650 }
651
652 /* Is it time to send another Discover? */
653 else if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod )
654 {
655 /* It is time to send another Discover. Increase the time
656 * period, and if it has not got to the point of giving up - send
657 * another discovery. */
658 EP_DHCPData.xDHCPTxPeriod <<= 1;
659
660 if( EP_DHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )
661 {
662 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
663 /* Make sure that the DHCP solicit request will be sent. */
664 EP_DHCPData.eDHCPState = eWaitingSendFirstDiscover;
665 prvSendDHCPMessage( pxEndPoint );
666 EP_DHCPData.eDHCPState = eWaitingOffer;
667 FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", EP_DHCPData.xDHCPTxPeriod ) );
668 }
669 else
670 {
671 FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", EP_DHCPData.xDHCPTxPeriod, ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) );
672
673 xGivingUp = pdTRUE;
674 }
675 }
676 else
677 {
678 /* There was no DHCP reply, there was no time-out, just keep on waiting. */
679 }
680
681 break;
682
683 case eWaitingAcknowledge:
684
685 if( pxDHCPMessage->uxMessageType == DHCPv6_message_Type_Reply )
686 {
687 /* DHCP completed. The IP address can now be used, and the
688 * timer set to the lease timeout time. */
689 vDHCPv6ProcessEndPoint_HandleReply( pxEndPoint, pxDHCPMessage );
690 }
691 else if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod )
692 {
693 /* It is time to send another Request. Increase the time
694 * period, and if it has not got to the point of giving up - send
695 * another discovery. */
696 EP_DHCPData.xDHCPTxPeriod <<= 1;
697
698 if( EP_DHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )
699 {
700 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
701 /* Make sure that the DHCPv6_message_Type_Request will be sent. */
702 EP_DHCPData.eDHCPState = eWaitingOffer;
703 prvSendDHCPMessage( pxEndPoint );
704 EP_DHCPData.eDHCPState = eWaitingAcknowledge;
705 FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", EP_DHCPData.xDHCPTxPeriod ) );
706 }
707 else
708 {
709 /* Give up, start again. */
710 EP_DHCPData.eDHCPState = eInitialWait;
711 FreeRTOS_debug_printf( ( "vDHCPProcess: timeout period is out of bound, restart DHCPv6\n" ) );
712 vDHCP_RATimerReload( pxEndPoint, dhcpINITIAL_TIMER_PERIOD );
713 }
714 }
715
716 break;
717
718 case eLeasedAddress:
719
720 /* Resend the request at the appropriate time to renew the lease. */
721 prvCreateDHCPv6Socket( pxEndPoint );
722
723 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
724 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
725 prvSendDHCPMessage( pxEndPoint );
726 EP_DHCPData.eDHCPState = eWaitingAcknowledge;
727
728 /* From now on, we should be called more often */
729 vDHCP_RATimerReload( pxEndPoint, dhcpINITIAL_TIMER_PERIOD );
730
731 break;
732
733 case eNotUsingLeasedAddress:
734
735 vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE );
736 break;
737
738 default:
739 /* Lint: all options are included. */
740 break;
741 }
742
743 return xGivingUp;
744 }
745 /*-----------------------------------------------------------*/
746
747 /**
748 * @brief Run one cycle of the DHCP state machine.
749 *
750 * @param[in] xReset pdTRUE is the state machine has to be reset.
751 * @param[in] pxEndPoint The end-point that needs DHCP.
752 * @param[in] pxDHCPMessage A DHCP message that has just been received, or NULL.
753 */
vDHCPv6ProcessEndPoint(BaseType_t xReset,NetworkEndPoint_t * pxEndPoint,DHCPMessage_IPv6_t * pxDHCPMessage)754 static void vDHCPv6ProcessEndPoint( BaseType_t xReset,
755 NetworkEndPoint_t * pxEndPoint,
756 DHCPMessage_IPv6_t * pxDHCPMessage )
757 {
758 BaseType_t xGivingUp = pdFALSE;
759
760 /* Is DHCP starting over? */
761 if( xReset != pdFALSE )
762 {
763 EP_DHCPData.eDHCPState = eInitialWait;
764 }
765
766 if( ( EP_DHCPData.eDHCPState != EP_DHCPData.eExpectedState ) && ( xReset == pdFALSE ) )
767 {
768 /* When the DHCP event was generated, the DHCP client was
769 * in a different state. Therefore, ignore this event. */
770 FreeRTOS_debug_printf( ( "vDHCPv6ProcessEndPoint: wrong state: expect: %d got: %d : ignore\n",
771 EP_DHCPData.eExpectedState, EP_DHCPData.eDHCPState ) );
772 }
773 else
774 {
775 #if ( ipconfigHAS_DEBUG_PRINTF == 1 )
776 {
777 static eDHCPState_t lastState = eNotUsingLeasedAddress;
778
779 if( lastState != EP_DHCPData.eDHCPState )
780 {
781 lastState = EP_DHCPData.eDHCPState;
782 FreeRTOS_debug_printf( ( "vDHCPv6ProcessEndPoint: enter %s (%d)\n", prvStateName( EP_DHCPData.eDHCPState ), EP_DHCPData.eDHCPState ) );
783 }
784 }
785 #endif /* ( ipconfigHAS_DEBUG_PRINTF == 1 ) */
786
787 xGivingUp = xDHCPv6ProcessEndPoint_HandleState( pxEndPoint, pxDHCPMessage );
788
789 if( xGivingUp != pdFALSE )
790 {
791 FreeRTOS_debug_printf( ( "vDHCPv6ProcessEndPoint: Giving up\n" ) );
792
793 /* xGivingUp became true either because of a time-out, or because
794 * xApplicationDHCPHook_Multi() returned another value than 'eDHCPContinue',
795 * meaning that the conversion is cancelled from here. */
796
797 /* Revert to static IP address. */
798 taskENTER_CRITICAL();
799 {
800 ( void ) memcpy( EP_IPv6_SETTINGS.xIPAddress.ucBytes, pxEndPoint->ipv6_defaults.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
801 iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IPv6_ADDRESS( EP_IPv6_SETTINGS.xIPAddress );
802 }
803 taskEXIT_CRITICAL();
804
805 EP_DHCPData.eDHCPState = eNotUsingLeasedAddress;
806 vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE );
807
808 /* Close socket to ensure packets don't queue on it. */
809 prvCloseDHCPv6Socket( pxEndPoint );
810
811 /* DHCP failed, the default configured IP-address will be used. Now
812 * call vIPNetworkUpCalls() to send the network-up event and start the ARP
813 * timer. */
814 vIPNetworkUpCalls( pxEndPoint );
815 }
816 }
817 }
818 /*-----------------------------------------------------------*/
819
820 /**
821 * @brief Close the shared UDP/DHCP socket. This results in lowering the reference count.
822 * The last user of the socket will close it.
823 *
824 * @param[in] pxEndPoint The end-point that wants to close the socket.
825 */
prvCloseDHCPv6Socket(NetworkEndPoint_t * pxEndPoint)826 static void prvCloseDHCPv6Socket( NetworkEndPoint_t * pxEndPoint )
827 {
828 if( ( EP_DHCPData.xDHCPSocket == NULL ) || ( EP_DHCPData.xDHCPSocket != xDHCPv6Socket ) )
829 {
830 /* the socket can not be closed. */
831 }
832 else if( xDHCPv6SocketUserCount > 0 )
833 {
834 xDHCPv6SocketUserCount--;
835
836 if( xDHCPv6SocketUserCount == 0 )
837 {
838 /* This modules runs from the IP-task. Use the internal
839 * function 'vSocketClose()` to close the socket. */
840 ( void ) vSocketClose( xDHCPv6Socket );
841 xDHCPv6Socket = NULL;
842 }
843
844 EP_DHCPData.xDHCPSocket = NULL;
845 }
846 else
847 {
848 /* Strange: there is a socket, but there are no users. */
849 }
850
851 FreeRTOS_printf( ( "DHCP-socket[%02x-%02x]: closed, user count %d\n",
852 pxEndPoint->xMACAddress.ucBytes[ 4 ],
853 pxEndPoint->xMACAddress.ucBytes[ 5 ],
854 ( int ) xDHCPv6SocketUserCount ) );
855 }
856
857 /**
858 * @brief Return the UDP/DHCP socket, or create if it doesn't exist.
859 *
860 * @param[in] pxEndPoint The end-point that needs the socket.
861 */
prvCreateDHCPv6Socket(NetworkEndPoint_t * pxEndPoint)862 static void prvCreateDHCPv6Socket( NetworkEndPoint_t * pxEndPoint )
863 {
864 struct freertos_sockaddr xAddress;
865 BaseType_t xReturn;
866 TickType_t xTimeoutTime = ( TickType_t ) 0;
867
868 if( ( xDHCPv6Socket != NULL ) && ( EP_DHCPData.xDHCPSocket == xDHCPv6Socket ) )
869 {
870 /* the socket is still valid. */
871 }
872 else if( xDHCPv6Socket == NULL ) /* Create the socket, if it has not already been created. */
873 {
874 xDHCPv6Socket = FreeRTOS_socket( FREERTOS_AF_INET6, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
875 configASSERT( xSocketValid( xDHCPv6Socket ) == pdTRUE );
876
877 /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the
878 * context of the IP task. */
879 ( void ) FreeRTOS_setsockopt( xDHCPv6Socket, 0, FREERTOS_SO_RCVTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) );
880 ( void ) FreeRTOS_setsockopt( xDHCPv6Socket, 0, FREERTOS_SO_SNDTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) );
881
882 memset( &xAddress, 0, sizeof( xAddress ) );
883 xAddress.sin_family = FREERTOS_AF_INET6;
884 xAddress.sin_len = ( uint8_t ) sizeof( xAddress );
885 /* Bind to the standard DHCP client port. */
886 xAddress.sin_port = FreeRTOS_htons( ipDHCPv6_CLIENT_PORT );
887 xReturn = vSocketBind( xDHCPv6Socket, &xAddress, sizeof( xAddress ), pdFALSE );
888 configASSERT( xReturn == 0 );
889 xDHCPv6SocketUserCount = 1;
890 FreeRTOS_printf( ( "DHCP-socket[%02x-%02x]: DHCP Socket Create\n",
891 pxEndPoint->xMACAddress.ucBytes[ 4 ],
892 pxEndPoint->xMACAddress.ucBytes[ 5 ] ) );
893
894 ( void ) xReturn;
895 }
896 else
897 {
898 xDHCPv6SocketUserCount++;
899 }
900
901 EP_DHCPData.xDHCPSocket = xDHCPv6Socket;
902 }
903 /*-----------------------------------------------------------*/
904
905 /**
906 * @brief Initialise the DHCP state machine of a given end-point.
907 *
908 * @param[in] pxEndPoint The end-point.
909 */
prvInitialiseDHCPv6(NetworkEndPoint_t * pxEndPoint)910 static void prvInitialiseDHCPv6( NetworkEndPoint_t * pxEndPoint )
911 {
912 /* Initialise the parameters that will be set by the DHCP process. Per
913 * https://www.ietf.org/rfc/rfc2131.txt, Transaction ID should be a random
914 * value chosen by the client. */
915
916 /* Check for random number generator API failure. */
917 EP_DHCPData.ulOfferedIPAddress = 0U;
918 EP_DHCPData.ulDHCPServerAddress = 0U;
919 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
920 /* Force creating a new transaction ID. */
921 pxEndPoint->pxDHCPMessage->ucHasUID = 0U;
922
923 /* Create the DHCP socket if it has not already been created. */
924 prvCreateDHCPv6Socket( pxEndPoint );
925 FreeRTOS_debug_printf( ( "prvInitialiseDHCPv6: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) );
926 vDHCP_RATimerReload( pxEndPoint, dhcpINITIAL_TIMER_PERIOD );
927 }
928 /*-----------------------------------------------------------*/
929
930 /**
931 * @brief Send a DHCPv6 message to a DHCP server.
932 *
933 * @param[in] pxEndPoint The end-point for which a message will be sent.
934 */
prvSendDHCPMessage(NetworkEndPoint_t * pxEndPoint)935 static void prvSendDHCPMessage( NetworkEndPoint_t * pxEndPoint )
936 {
937 BaseType_t xRandomOk = pdTRUE;
938 DHCPMessage_IPv6_t * pxDHCPMessage = NULL;
939
940 configASSERT( pxEndPoint != NULL );
941
942 pxDHCPMessage = pxEndPoint->pxDHCPMessage;
943
944 if( pxDHCPMessage->ucHasUID == 0U )
945 {
946 uint32_t ulTransactionID = 0U;
947 uint32_t ulCurTime;
948
949 xRandomOk = xApplicationGetRandomNumber( &( ulTransactionID ) );
950 ulTransactionID &= 0xffffffU;
951
952 /* The application should supply the following time-function. */
953 ulCurTime = ulApplicationTimeHook();
954
955 pxDHCPMessage->ulTimeStamp = ulCurTime - ( uint32_t ) SECS_FROM_1970_TILL_2000;
956 pxDHCPMessage->ucHasUID = 1U;
957 pxDHCPMessage->ucTransactionID[ 0 ] = ( uint8_t ) ( ( ulTransactionID >> 16 ) & 0xffU );
958 pxDHCPMessage->ucTransactionID[ 1 ] = ( uint8_t ) ( ( ulTransactionID >> 8 ) & 0xffU );
959 pxDHCPMessage->ucTransactionID[ 2 ] = ( uint8_t ) ( ulTransactionID & 0xffU );
960 EP_DHCPData.ulTransactionId = ulTransactionID;
961 FreeRTOS_debug_printf( ( "Created transaction ID : 0x%06X\n", ulTransactionID ) );
962 }
963
964 if( ( xRandomOk == pdPASS ) && ( EP_DHCPData.xDHCPSocket != NULL ) )
965 {
966 BitConfig_t xMessage;
967 struct freertos_sockaddr xAddress;
968 uint8_t ucMessageType = 0;
969
970 /* Not useful, but MISRA issues a mandatory warning. */
971 ( void ) memset( &( xMessage ), 0, sizeof( xMessage ) );
972
973 if( xBitConfig_init( &( xMessage ), NULL, DHCPv6_SEND_MAX_BUFFER_SIZE ) == pdTRUE )
974 {
975 switch( EP_DHCPData.eDHCPState )
976 {
977 case eWaitingSendFirstDiscover:
978 ucMessageType = DHCPv6_message_Type_Solicit;
979 break;
980
981 case eWaitingOffer:
982 case eLeasedAddress:
983 ucMessageType = DHCPv6_message_Type_Request;
984 break;
985
986 default:
987 /* No message to be sent in this stage. */
988 break;
989 }
990
991 if( ucMessageType != 0U )
992 {
993 vBitConfig_write_8( &( xMessage ), ucMessageType ); /* 1 Solicit, 3, request */
994 vBitConfig_write_uc( &( xMessage ), pxDHCPMessage->ucTransactionID, 3 );
995
996 pxDHCPMessage->xClientID.usDUIDType = 1U;
997 pxDHCPMessage->xClientID.usHardwareType = 1U;
998
999 /* DHCPv6_Option_Client_Identifier */
1000 vBitConfig_write_16( &( xMessage ), DHCPv6_Option_Client_Identifier ); /* Option is 1: Client Identifier */
1001 vBitConfig_write_16( &( xMessage ), 14U ); /* The length is 14 */
1002 vBitConfig_write_16( &( xMessage ), pxDHCPMessage->xClientID.usDUIDType ); /* 1 : Link Layer address + time */
1003 vBitConfig_write_16( &( xMessage ), pxDHCPMessage->xClientID.usHardwareType ); /* 1 : Ethernet */
1004 vBitConfig_write_32( &( xMessage ), pxDHCPMessage->ulTimeStamp ); /* DUID Time: seconds since 1-1-2000. */
1005 vBitConfig_write_uc( &( xMessage ), pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ); /* Link Layer address, 6 bytes */
1006 ( void ) pucBitConfig_peek_last_index_uc( &( xMessage ), EP_DHCPData.ucClientDUID, dhcpIPv6_CLIENT_DUID_LENGTH );
1007
1008 if( pxDHCPMessage->xServerID.uxLength != 0U )
1009 {
1010 uint16_t usLength = ( uint16_t ) pxDHCPMessage->xServerID.uxLength;
1011 /* DHCPv6_Option_Server_Identifier */
1012 vBitConfig_write_16( &( xMessage ), DHCPv6_Option_Server_Identifier ); /* Option is 1: Server Identifier */
1013 vBitConfig_write_16( &( xMessage ), ( uint16_t ) ( usLength + 4U ) ); /* The length is 14 */
1014 vBitConfig_write_16( &( xMessage ), pxDHCPMessage->xServerID.usDUIDType ); /* The type of DUID: 1, 2, or 3. */
1015 vBitConfig_write_16( &( xMessage ), pxDHCPMessage->xServerID.usHardwareType );
1016 vBitConfig_write_uc( &( xMessage ), pxDHCPMessage->xServerID.pucID, pxDHCPMessage->xServerID.uxLength );
1017 }
1018
1019 if( ( EP_DHCPData.eDHCPState == eWaitingOffer ) || ( EP_DHCPData.eDHCPState == eLeasedAddress ) )
1020 {
1021 /* DHCPv6_Option_Option_List */
1022 vBitConfig_write_16( &( xMessage ), DHCPv6_Option_Option_List ); /* usOption; Option is 6 */
1023 vBitConfig_write_16( &( xMessage ), 4U ); /* usLength; length is 4 */
1024 vBitConfig_write_16( &( xMessage ), DHCP6_OPTION_REQUEST_DNS ); /* usOption_1; 00 17 : DNS Recursive name server. */
1025 vBitConfig_write_16( &( xMessage ), DHCP6_OPTION_REQUEST_DOMAIN_SEARCH_LIST ); /* usOption_2; 00 18 : Domain search list. */
1026 }
1027
1028 /* DHCPv6_Option_Elapsed_Time */
1029 vBitConfig_write_16( &( xMessage ), DHCPv6_Option_Elapsed_Time ); /* usOption; Option is 8 * / */
1030 vBitConfig_write_16( &( xMessage ), 2U ); /* usLength; length is 2 * / */
1031 vBitConfig_write_16( &( xMessage ), 0x0000 ); /* usTime; 00 00 : 0 ms. * / */
1032
1033 /* DHCPv6_Option_IA_for_Prefix_Delegation */
1034 uint32_t ulIAID = 0x27fe8f95;
1035 uint32_t ulTime_1 = 3600U;
1036 uint32_t ulTime_2 = 5400U;
1037
1038 vBitConfig_write_16( &( xMessage ), DHCPv6_Option_IA_for_Prefix_Delegation ); /* usOption; Option is 25 */
1039 vBitConfig_write_16( &( xMessage ), 41 ); /* usLength; length is 12 + 29 = 41 */
1040 vBitConfig_write_32( &( xMessage ), ulIAID ); /* 27 fe 8f 95. */
1041 vBitConfig_write_32( &( xMessage ), ulTime_1 ); /* 00 00 0e 10: 3600 sec */
1042 vBitConfig_write_32( &( xMessage ), ulTime_2 ); /* 00 00 15 18: 5400 sec */
1043
1044 /* DHCPv6_Option_IA_Prefix */
1045 uint32_t ulPreferredLifeTime = 4500U;
1046 uint32_t ulPValidLifeTime = 7200U;
1047 uint8_t ucPrefixLength = ( uint8_t ) pxEndPoint->ipv6_settings.uxPrefixLength;
1048
1049 vBitConfig_write_16( &( xMessage ), DHCPv6_Option_IA_Prefix ); /* usOption Option is 26 */
1050 vBitConfig_write_16( &( xMessage ), 25 ); /* usLength length is 25 */
1051 vBitConfig_write_32( &( xMessage ), ulPreferredLifeTime ); /* 4500 */
1052 vBitConfig_write_32( &( xMessage ), ulPValidLifeTime ); /* e.g. 7200 seconds. */
1053 vBitConfig_write_8( &( xMessage ), ucPrefixLength ); /* e.g. 64 bits */
1054 vBitConfig_write_uc( &( xMessage ), pxEndPoint->ipv6_settings.xPrefix.ucBytes, ipSIZE_OF_IPv6_ADDRESS ); /* 2001:0:0:fe00:: */
1055
1056 vBitConfig_write_16( &( xMessage ), DHCPv6_Option_NonTemporaryAddress ); /* usOption Option is 3 */
1057 vBitConfig_write_16( &( xMessage ), 12 ); /* usLength length is 12 */
1058 vBitConfig_write_32( &( xMessage ), ulIAID ); /* 27 fe 8f 95. */
1059 vBitConfig_write_32( &( xMessage ), ulPreferredLifeTime ); /* 4500 */
1060 vBitConfig_write_32( &( xMessage ), ulPValidLifeTime ); /* 7200 */
1061
1062 if( ( EP_DHCPData.eDHCPState == eWaitingOffer ) || ( EP_DHCPData.eDHCPState == eLeasedAddress ) )
1063 {
1064 vBitConfig_write_16( &( xMessage ), DHCPv6_Option_DNS_recursive_name_server ); /* usOption Option is 23 */
1065 vBitConfig_write_16( &( xMessage ), 0U ); /* usLength length is 0 */
1066 }
1067
1068 ( void ) memset( &( xAddress ), 0, sizeof xAddress );
1069 ( void ) FreeRTOS_inet_pton6( "ff02::1:2", xAddress.sin_address.xIP_IPv6.ucBytes );
1070 xAddress.sin_len = ( uint8_t ) sizeof xAddress; /* length of this structure. */
1071 xAddress.sin_family = FREERTOS_AF_INET6;
1072 xAddress.sin_port = FreeRTOS_htons( ipDHCPv6_SERVER_PORT );
1073
1074 struct freertos_sockaddr * pxAddress = &( xAddress );
1075
1076 FreeRTOS_printf( ( "DHCP Sending request %u.\n", ucMessageType ) );
1077 ( void ) FreeRTOS_sendto( EP_DHCPData.xDHCPSocket, ( const void * ) xMessage.ucContents, xMessage.uxIndex, 0, pxAddress, sizeof xAddress );
1078 }
1079
1080 vBitConfig_release( &( xMessage ) );
1081 } /* if( xBitConfig_init( &( xMessage ), NULL, 256 ) == pdTRUE ) */
1082 } /* ( xRandomOk == pdPASS ) && ( EP_DHCPData.xDHCPSocket != NULL ) */
1083 }
1084 /*-----------------------------------------------------------*/
1085
1086 /**
1087 * @brief Either the option 'NonTemporaryAddress' or 'IA_for_Prefix_Delegation'
1088 * was received. This function will read sub options like 'IA_Address',
1089 * IA_Prefix, and Status_Code.
1090 * It parses the raw message and fills 'pxDHCPMessage'.
1091 * @param[in] usOption The DHCPv6 option that was received.
1092 * @param[in] pxSet It contains the length and offset of the DHCP option.
1093 * @param[out] pxDHCPMessage it will be filled with the information from the option.
1094 * @param[in] pxMessage The raw packet as it was received.
1095 *
1096 * @return It returns pdTRUE in case the option parsed successfully, otherwise pdFALSE.
1097 */
prvDHCPv6_subOption(uint16_t usOption,const DHCPOptionSet_t * pxSet,DHCPMessage_IPv6_t * pxDHCPMessage,BitConfig_t * pxMessage)1098 static BaseType_t prvDHCPv6_subOption( uint16_t usOption,
1099 const DHCPOptionSet_t * pxSet,
1100 DHCPMessage_IPv6_t * pxDHCPMessage,
1101 BitConfig_t * pxMessage )
1102 {
1103 uint32_t ulIAID = ulBitConfig_read_32( pxMessage );
1104 uint32_t ulTime_1 = ulBitConfig_read_32( pxMessage );
1105 uint32_t ulTime_2 = ulBitConfig_read_32( pxMessage );
1106 size_t uxUsed = pxMessage->uxIndex - pxSet->uxStart;
1107 size_t uxRemain = 0U;
1108 uint16_t usOption2;
1109 uint16_t uxLength2;
1110 BaseType_t xReturn = pdTRUE;
1111
1112 ( void ) ulIAID;
1113 ( void ) ulTime_1;
1114 ( void ) ulTime_2;
1115 ( void ) usOption;
1116
1117 if( pxSet->uxOptionLength >= uxUsed )
1118 {
1119 uxRemain = pxSet->uxOptionLength - uxUsed;
1120 }
1121 else
1122 {
1123 FreeRTOS_printf( ( "prvDHCPv6_subOption: Option %u used length %lu is larger than option length %lu\n", usOption, uxUsed, pxSet->uxOptionLength ) );
1124 xReturn = pdFALSE;
1125 }
1126
1127 while( uxRemain > 0U )
1128 {
1129 if( uxRemain < 4U )
1130 {
1131 /* Sub-option length is always larger than 4 to store option code and length. */
1132 FreeRTOS_printf( ( "prvDHCPv6_subOption: %s has invalid option with length %lu\n",
1133 ( usOption == DHCPv6_Option_NonTemporaryAddress ) ? "Address assignment" : "Prefix Delegation",
1134 uxRemain ) );
1135 xReturn = pdFALSE;
1136 break;
1137 }
1138
1139 usOption2 = usBitConfig_read_16( pxMessage );
1140 uxLength2 = usBitConfig_read_16( pxMessage );
1141
1142 uxUsed = pxMessage->uxIndex - pxSet->uxStart;
1143
1144 /* Check sub-option length. */
1145 if( prvIsOptionLengthValid( usOption2, uxLength2, pxMessage->uxSize - pxMessage->uxIndex ) != pdTRUE )
1146 {
1147 FreeRTOS_printf( ( "prvDHCPv6_subOption: %u has invalid length %u, remaining buffer size %lu\n",
1148 usOption2,
1149 uxLength2,
1150 pxMessage->uxSize - pxMessage->uxIndex ) );
1151 xReturn = pdFALSE;
1152 break;
1153 }
1154
1155 switch( usOption2 )
1156 {
1157 case DHCPv6_Option_IA_Address:
1158 ( void ) xBitConfig_read_uc( pxMessage, pxDHCPMessage->xIPAddress.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
1159 pxDHCPMessage->ulPreferredLifeTime = ulBitConfig_read_32( pxMessage );
1160 pxDHCPMessage->ulValidLifeTime = ulBitConfig_read_32( pxMessage );
1161 FreeRTOS_printf( ( "IP Address %pip\n", ( void * ) pxDHCPMessage->xIPAddress.xIP_IPv6.ucBytes ) );
1162 break;
1163
1164 case DHCPv6_Option_IA_Prefix:
1165 pxDHCPMessage->ulPreferredLifeTime = ulBitConfig_read_32( pxMessage );
1166 pxDHCPMessage->ulValidLifeTime = ulBitConfig_read_32( pxMessage );
1167 pxDHCPMessage->ucprefixLength = ucBitConfig_read_8( pxMessage );
1168 ( void ) xBitConfig_read_uc( pxMessage, pxDHCPMessage->xPrefixAddress.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
1169 FreeRTOS_printf( ( "Address prefix: %pip length %d\n", ( void * ) pxDHCPMessage->xPrefixAddress.xIP_IPv6.ucBytes, pxDHCPMessage->ucprefixLength ) );
1170 break;
1171
1172 case DHCPv6_Option_Status_Code:
1173 xReturn = prvDHCPv6_handleStatusCode( uxLength2,
1174 pxMessage );
1175 break;
1176
1177 default:
1178 ( void ) xBitConfig_read_uc( pxMessage, NULL, uxLength2 - uxUsed );
1179 FreeRTOS_printf( ( "prvDHCPv6Analyse: skipped unknown option %u\n", usOption2 ) );
1180 break;
1181 }
1182
1183 if( xReturn != pdTRUE )
1184 {
1185 FreeRTOS_printf( ( "prvDHCPv6_subOption: One of sub-options %d returns error\n", usOption2 ) );
1186 break;
1187 }
1188
1189 /* Update remaining length. */
1190 uxUsed = pxMessage->uxIndex - pxSet->uxStart;
1191 uxRemain = 0U;
1192
1193 if( pxSet->uxOptionLength > uxUsed )
1194 {
1195 uxRemain = pxSet->uxOptionLength - uxUsed;
1196 }
1197 }
1198
1199 return xReturn;
1200 }
1201 /*-----------------------------------------------------------*/
1202
1203 /**
1204 * @brief A DHCP packet has a list of options, each one starting with a type and a length
1205 * field. This function parses a single DHCP option.
1206 * @param[in] pxEndPoint The end-point that wants a DHCPv6 address.
1207 * @param[in] usOption IPv6 DHCP option to be handled.
1208 * @param[in] pxSet It contains the length and offset of the DHCP option.
1209 * @param[out] pxDHCPMessage it will be filled with the information from the option.
1210 * @param[in] pxMessage The raw packet as it was received.
1211 */
prvDHCPv6_handleOption(struct xNetworkEndPoint * pxEndPoint,uint16_t usOption,const DHCPOptionSet_t * pxSet,DHCPMessage_IPv6_t * pxDHCPMessage,BitConfig_t * pxMessage)1212 static BaseType_t prvDHCPv6_handleOption( struct xNetworkEndPoint * pxEndPoint,
1213 uint16_t usOption,
1214 const DHCPOptionSet_t * pxSet,
1215 DHCPMessage_IPv6_t * pxDHCPMessage,
1216 BitConfig_t * pxMessage )
1217 {
1218 BaseType_t xReady = pdFALSE;
1219 int32_t lIDSize = 0;
1220 uint8_t ucClientDUID[ dhcpIPv6_CLIENT_DUID_LENGTH ];
1221
1222 if( prvIsOptionLengthValid( usOption, pxSet->uxOptionLength, pxMessage->uxSize - pxMessage->uxIndex ) != pdTRUE )
1223 {
1224 FreeRTOS_printf( ( "prvDHCPv6_handleOption: Option %u has invalid length %lu, remaining buffer size %lu\n",
1225 usOption,
1226 pxSet->uxOptionLength,
1227 pxMessage->uxSize - pxMessage->uxIndex ) );
1228 xReady = pdTRUE;
1229 }
1230
1231 if( xReady == pdFALSE )
1232 {
1233 switch( usOption )
1234 {
1235 case DHCPv6_Option_Status_Code:
1236
1237 if( prvDHCPv6_handleStatusCode( pxSet->uxOptionLength, pxMessage ) != pdTRUE )
1238 {
1239 pxMessage->xHasError = pdTRUE_UNSIGNED;
1240 }
1241
1242 break;
1243
1244 case DHCPv6_Option_Client_Identifier:
1245 lIDSize = ( int32_t ) ( pxSet->uxOptionLength ) - 4;
1246
1247 if( lIDSize >= 0 )
1248 {
1249 /*
1250 * 1 : Link-layer address plus time (DUID-LLT)
1251 * 2 : Vendor-assigned unique ID based on Enterprise Number (DUID-EN)
1252 * 3 : Link-layer address (DUID-LL)
1253 */
1254 pxDHCPMessage->xClientID.uxLength = ( size_t ) lIDSize;
1255 pxDHCPMessage->xClientID.usDUIDType = usBitConfig_read_16( pxMessage ); /* 0x0001 : Link Layer address + time */
1256 pxDHCPMessage->xClientID.usHardwareType = usBitConfig_read_16( pxMessage ); /* 1 = Ethernet. */
1257
1258 if( ( size_t ) lIDSize <= sizeof( pxDHCPMessage->xClientID.pucID ) )
1259 {
1260 /* Refer to RFC3315 - sec 15.3, we need to discard packets with following conditions:
1261 * - the message does not include a Server Identifier option.
1262 * - the message does not include a Client Identifier option.
1263 * - the contents of the Client Identifier option does not match the client's DUID.
1264 * - the "transaction-id" field value does not match the value the client used in its Solicit message. */
1265 ( void ) xBitConfig_read_uc( pxMessage, pxDHCPMessage->xClientID.pucID, ( size_t ) lIDSize ); /* Link Layer address, 6 bytes */
1266
1267 /* Check client DUID. */
1268 if( ( pxSet->uxOptionLength != dhcpIPv6_CLIENT_DUID_LENGTH ) ||
1269 ( pucBitConfig_peek_last_index_uc( pxMessage, ucClientDUID, pxSet->uxOptionLength ) != pdTRUE ) ||
1270 ( memcmp( ucClientDUID, EP_DHCPData.ucClientDUID, dhcpIPv6_CLIENT_DUID_LENGTH ) != 0 ) )
1271 {
1272 FreeRTOS_printf( ( "prvDHCPv6Analyse: wrong client ID\n" ) );
1273 pxMessage->xHasError = pdTRUE;
1274 }
1275 }
1276 else
1277 {
1278 /* The value of DHCPv6_Option_Client_Identifier is invalid. */
1279 FreeRTOS_printf( ( "prvDHCPv6Analyse: client ID too long\n" ) );
1280 xReady = pdTRUE;
1281 }
1282 }
1283 else
1284 {
1285 /* The value of DHCPv6_Option_Client_Identifier is invalid. */
1286 FreeRTOS_printf( ( "prvDHCPv6Analyse: client ID too short\n" ) );
1287 xReady = pdTRUE;
1288 }
1289
1290 break;
1291
1292 case DHCPv6_Option_Server_Identifier:
1293 lIDSize = ( int32_t ) ( pxSet->uxOptionLength ) - 4;
1294
1295 if( lIDSize >= 0 )
1296 {
1297 /*
1298 * 1 : Link-layer address plus time (DUID-LLT)
1299 * 2 : Vendor-assigned unique ID based on Enterprise Number (DUID-EN)
1300 * 3 : Link-layer address (DUID-LL)
1301 */
1302 pxDHCPMessage->xServerID.uxLength = ( size_t ) lIDSize;
1303 pxDHCPMessage->xServerID.usDUIDType = usBitConfig_read_16( pxMessage ); /* 0x0001 : Link Layer address + time */
1304 pxDHCPMessage->xServerID.usHardwareType = usBitConfig_read_16( pxMessage ); /* 1 = Ethernet. */
1305
1306 if( ( size_t ) lIDSize <= sizeof( pxDHCPMessage->xServerID.pucID ) )
1307 {
1308 ( void ) xBitConfig_read_uc( pxMessage, pxDHCPMessage->xServerID.pucID, ( size_t ) lIDSize ); /* Link Layer address, 6 bytes */
1309 }
1310 else
1311 {
1312 FreeRTOS_printf( ( "prvDHCPv6Analyse: server ID too long\n" ) );
1313 xReady = pdTRUE;
1314 }
1315 }
1316 else
1317 {
1318 /* The value of DHCPv6_Option_Server_Identifier is invalid. */
1319 FreeRTOS_printf( ( "prvDHCPv6Analyse: server ID too short\n" ) );
1320 xReady = pdTRUE;
1321 }
1322
1323 break;
1324
1325 case DHCPv6_Option_Preference:
1326 {
1327 uint8_t ucPreference = ucBitConfig_read_8( pxMessage );
1328 ( void ) ucPreference;
1329 break;
1330 }
1331
1332 case DHCPv6_Option_DNS_recursive_name_server:
1333 {
1334 size_t uDNSCount;
1335
1336 if( ( pxSet->uxOptionLength == 0U ) || ( ( pxSet->uxOptionLength % ipSIZE_OF_IPv6_ADDRESS ) != 0U ) )
1337 {
1338 FreeRTOS_printf( ( "prvDHCPv6Analyse: bad DNS length\n" ) );
1339 xReady = pdTRUE;
1340 break;
1341 }
1342
1343 uDNSCount = pxSet->uxOptionLength / ipSIZE_OF_IPv6_ADDRESS;
1344
1345 while( uDNSCount > 0U )
1346 {
1347 if( pxDHCPMessage->uxDNSCount < ipconfigENDPOINT_DNS_ADDRESS_COUNT )
1348 {
1349 ( void ) xBitConfig_read_uc( pxMessage, pxDHCPMessage->xDNSServers[ pxDHCPMessage->uxDNSCount ].xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
1350 pxDHCPMessage->uxDNSCount++;
1351 }
1352 else
1353 {
1354 /* The DNS address can not be stored. Just advance the pointer. */
1355 ( void ) xBitConfig_read_uc( pxMessage, NULL, ipSIZE_OF_IPv6_ADDRESS );
1356 }
1357
1358 uDNSCount--;
1359 }
1360
1361 break;
1362 }
1363
1364 case DHCPv6_Option_Domain_Search_List:
1365 ( void ) xBitConfig_read_uc( pxMessage, NULL, pxSet->uxOptionLength );
1366 break;
1367
1368 case DHCPv6_Option_NonTemporaryAddress:
1369 case DHCPv6_Option_IA_for_Prefix_Delegation:
1370
1371 if( prvDHCPv6_subOption( usOption, pxSet, pxDHCPMessage, pxMessage ) != pdTRUE )
1372 {
1373 FreeRTOS_printf( ( "prvDHCPv6_handleOption: prvDHCPv6_subOption returns error.\n" ) );
1374 xReady = pdTRUE;
1375 }
1376
1377 break;
1378
1379 default:
1380 FreeRTOS_printf( ( "prvDHCPv6Analyse: Option %u not implemented\n", usOption ) );
1381 ( void ) xBitConfig_read_uc( pxMessage, NULL, pxSet->uxOptionLength );
1382 break;
1383 }
1384 }
1385
1386 return xReady;
1387 }
1388 /*-----------------------------------------------------------*/
1389
1390 /**
1391 * @brief Analyse the reply from a DHCP server.
1392 *
1393 * @param[in] pxEndPoint The end-point that wants a DHCPv6 address.
1394 * @param[in] pucAnswer The payload text of the incoming packet.
1395 * @param[in] uxTotalLength The number of valid bytes in pucAnswer.
1396 * @param[in] pxDHCPMessage The DHCP object of the end-point.
1397 *
1398 * @return pdTRUE if the analysis was successful.
1399 */
prvDHCPv6Analyse(struct xNetworkEndPoint * pxEndPoint,const uint8_t * pucAnswer,size_t uxTotalLength,DHCPMessage_IPv6_t * pxDHCPMessage)1400 static BaseType_t prvDHCPv6Analyse( struct xNetworkEndPoint * pxEndPoint,
1401 const uint8_t * pucAnswer,
1402 size_t uxTotalLength,
1403 DHCPMessage_IPv6_t * pxDHCPMessage )
1404 {
1405 BitConfig_t xMessage;
1406 BaseType_t xResult = pdPASS;
1407 uint32_t ulOptionsReceived = 0U;
1408
1409 /* Allocate enough space in 'xMessage' and clear it. */
1410 if( xBitConfig_init( &xMessage, pucAnswer, uxTotalLength ) != pdFAIL )
1411 {
1412 BaseType_t xReady = pdFALSE;
1413
1414 ( void ) memset( pxDHCPMessage, 0, sizeof( *pxDHCPMessage ) );
1415
1416 pxDHCPMessage->uxMessageType = ucBitConfig_read_8( &xMessage );
1417 ( void ) xBitConfig_read_uc( &xMessage, pxDHCPMessage->ucTransactionID, 3 );
1418
1419 pxDHCPMessage->ulTransactionID =
1420 ( ( ( uint32_t ) pxDHCPMessage->ucTransactionID[ 0 ] ) << 16 ) |
1421 ( ( ( uint32_t ) pxDHCPMessage->ucTransactionID[ 1 ] ) << 8 ) |
1422 ( ( ( uint32_t ) pxDHCPMessage->ucTransactionID[ 2 ] ) );
1423
1424 if( EP_DHCPData.ulTransactionId != pxDHCPMessage->ulTransactionID )
1425 {
1426 FreeRTOS_printf( ( "prvDHCPv6Analyse: Message %u transaction ID 0x%06X is different from sent ID 0x%06X\n",
1427 ( unsigned ) pxDHCPMessage->uxMessageType,
1428 ( unsigned ) pxDHCPMessage->ulTransactionID,
1429 EP_DHCPData.ulTransactionId ) );
1430
1431 xResult = pdFAIL;
1432 }
1433
1434 FreeRTOS_printf( ( "prvDHCPv6Analyse: Message %u ID theirs 0x%06X\n",
1435 ( unsigned ) pxDHCPMessage->uxMessageType,
1436 ( unsigned ) pxDHCPMessage->ulTransactionID ) );
1437
1438 switch( pxDHCPMessage->uxMessageType )
1439 {
1440 case DHCPv6_message_Type_Advertise:
1441 case DHCPv6_message_Type_Confirm:
1442 case DHCPv6_message_Type_Reply:
1443 case DHCPv6_message_Type_Decline:
1444 break;
1445
1446 default:
1447 FreeRTOS_printf( ( "prvDHCPv6Analyse: Can not handle message type %u\n",
1448 ( unsigned ) pxDHCPMessage->uxMessageType ) );
1449 xResult = pdFAIL;
1450 break;
1451 }
1452
1453 if( xResult == pdFAIL )
1454 {
1455 /* Ignore the reply because the message type is wrong. */
1456 xReady = pdTRUE;
1457 }
1458 else if( xMessage.xHasError != pdFALSE )
1459 {
1460 xReady = pdTRUE;
1461 xResult = pdFAIL;
1462 }
1463 else
1464 {
1465 /* xResult still equals 'pdPASS', carry on. */
1466 }
1467
1468 while( xReady == pdFALSE )
1469 {
1470 uint16_t usOption;
1471 DHCPOptionSet_t xSet;
1472
1473 ( void ) memset( &( xSet ), 0, sizeof( xSet ) );
1474
1475 usOption = usBitConfig_read_16( &xMessage );
1476 xSet.uxOptionLength = ( size_t ) usBitConfig_read_16( &xMessage );
1477 xSet.uxStart = xMessage.uxIndex;
1478
1479 if( xMessage.xHasError != pdFALSE )
1480 {
1481 FreeRTOS_printf( ( "prvDHCPv6Analyse: bad input\n" ) );
1482 xReady = pdTRUE;
1483 xResult = pdFAIL;
1484 }
1485 else
1486 {
1487 ulOptionsReceived |= ( ( ( uint32_t ) 1U ) << usOption );
1488 xReady = prvDHCPv6_handleOption( pxEndPoint, usOption, &( xSet ), pxDHCPMessage, &( xMessage ) );
1489 }
1490
1491 if( xMessage.xHasError != pdFALSE )
1492 {
1493 FreeRTOS_printf( ( "prvDHCPv6Analyse: bad input\n" ) );
1494 xReady = pdTRUE;
1495 xResult = pdFAIL;
1496 }
1497 else if( xReady == pdTRUE )
1498 {
1499 /* xReady might be set to pdTRUE by prvDHCPv6_handleOption when there happens any error on parsing options. */
1500 FreeRTOS_printf( ( "prvDHCPv6Analyse: prvDHCPv6_handleOption returns error.\n" ) );
1501 xResult = pdFAIL;
1502 }
1503 else if( xMessage.uxIndex == xMessage.uxSize )
1504 {
1505 xReady = pdTRUE;
1506 }
1507 else
1508 {
1509 /* More options will follow. */
1510 }
1511 } /* while( xReady == pdFALSE ) */
1512
1513 vBitConfig_release( &( xMessage ) );
1514
1515 if( xResult == pdPASS )
1516 {
1517 ulOptionsReceived &= dhcpMANDATORY_OPTIONS;
1518
1519 if( ulOptionsReceived != dhcpMANDATORY_OPTIONS )
1520 {
1521 xResult = pdFAIL;
1522 FreeRTOS_printf( ( "prvDHCPv6Analyse: Missing options: %X not equal to %X\n", ( unsigned int ) ulOptionsReceived, ( unsigned int ) dhcpMANDATORY_OPTIONS ) );
1523 }
1524 }
1525 } /* if( xBitConfig_init() ) */
1526 else
1527 {
1528 xResult = pdFAIL;
1529 }
1530
1531 return xResult;
1532 }
1533 /*-----------------------------------------------------------*/
1534
1535 /**
1536 * @brief transform a state number in a descriptive string.
1537 *
1538 * @param[in] eState The DHCP state number.
1539 *
1540 * @return A descriptive string.
1541 */
1542 #if ( ipconfigHAS_DEBUG_PRINTF == 1 )
prvStateName(eDHCPState_t eState)1543 static const char * prvStateName( eDHCPState_t eState )
1544 {
1545 const char * pcName;
1546
1547 switch( eState )
1548 {
1549 case eInitialWait:
1550 pcName = "eInitialWait";
1551 break;
1552
1553 case eWaitingSendFirstDiscover:
1554 pcName = "eWaitingSendFirstDiscover";
1555 break;
1556
1557 case eWaitingOffer:
1558 pcName = "eWaitingOffer";
1559 break;
1560
1561 case eWaitingAcknowledge:
1562 pcName = "eWaitingAcknowledge";
1563 break;
1564
1565 case eLeasedAddress:
1566 pcName = "eLeasedAddress";
1567 break;
1568
1569 case eNotUsingLeasedAddress:
1570 pcName = "eNotUsingLeasedAddress";
1571 break;
1572
1573 case eSendDHCPRequest:
1574 pcName = "eSendDHCPRequest";
1575 break;
1576
1577 default:
1578 pcName = "Unknown state";
1579 break;
1580 }
1581
1582 return pcName;
1583 }
1584 #endif /* ( ipconfigHAS_DEBUG_PRINTF == 1 ) */
1585
1586 /* *INDENT-OFF* */
1587 #endif /* ( ipconfigUSE_IPv6 != 0 ) && ( ipconfigUSE_DHCPv6 != 0 ) */
1588 /* *INDENT-ON* */
1589