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_DHCP.c 30 * @brief Implements the Dynamic Host Configuration Protocol for the FreeRTOS+TCP network stack. 31 */ 32 33 /* Standard includes. */ 34 #include <stdint.h> 35 36 /* FreeRTOS includes. */ 37 #include "FreeRTOS.h" 38 #include "task.h" 39 #include "semphr.h" 40 41 /* FreeRTOS+TCP includes. */ 42 #include "FreeRTOS_IP.h" 43 #include "FreeRTOS_Sockets.h" 44 #include "FreeRTOS_IP_Private.h" 45 #include "FreeRTOS_UDP_IP.h" 46 #include "FreeRTOS_DHCP.h" 47 #include "FreeRTOS_ARP.h" 48 #include "FreeRTOS_IP_Timers.h" 49 50 51 /* Exclude the entire file if DHCP is not enabled. */ 52 #if ( ipconfigUSE_DHCP != 0 ) 53 54 #include "NetworkInterface.h" 55 #include "NetworkBufferManagement.h" 56 57 #include "FreeRTOS_Routing.h" 58 59 /* The following define is temporary and serves to make the /single source 60 * code more similar to the /multi version. */ 61 62 #define EP_DHCPData pxEndPoint->xDHCPData /**< Temporary define to make /single source similar to /multi version. */ 63 #define EP_IPv4_SETTINGS pxEndPoint->ipv4_settings /**< Temporary define to make /single source similar to /multi version. */ 64 65 66 67 /** @brief The UDP socket used for all incoming and outgoing DHCP traffic. */ 68 _static Socket_t xDHCPv4Socket; 69 70 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 71 /* Define the Link Layer IP address: 169.254.x.x */ 72 #define LINK_LAYER_ADDRESS_0 169 73 #define LINK_LAYER_ADDRESS_1 254 74 75 /* Define the netmask used: 255.255.0.0 */ 76 #define LINK_LAYER_NETMASK_0 255 77 #define LINK_LAYER_NETMASK_1 255 78 #define LINK_LAYER_NETMASK_2 0 79 #define LINK_LAYER_NETMASK_3 0 80 #endif 81 82 /*-----------------------------------------------------------*/ 83 84 /** 85 * @brief The number of end-points that are making use of the UDP-socket. 86 */ 87 static BaseType_t xDHCPSocketUserCount = 0; 88 89 /* 90 * Generate a DHCP discover message and send it on the DHCP socket. 91 */ 92 static BaseType_t prvSendDHCPDiscover( NetworkEndPoint_t * pxEndPoint ); 93 94 /* 95 * Interpret message received on the DHCP socket. 96 */ 97 static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType, 98 NetworkEndPoint_t * pxEndPoint ); 99 100 /* 101 * Generate a DHCP request packet, and send it on the DHCP socket. 102 */ 103 static BaseType_t prvSendDHCPRequest( NetworkEndPoint_t * pxEndPoint ); 104 105 /* 106 * Prepare to start a DHCP transaction. This initialises some state variables 107 * and creates the DHCP socket if necessary. 108 */ 109 static void prvInitialiseDHCP( NetworkEndPoint_t * pxEndPoint ); 110 111 /* 112 * Creates the part of outgoing DHCP messages that are common to all outgoing 113 * DHCP messages. 114 */ 115 static uint8_t * prvCreatePartDHCPMessage( struct freertos_sockaddr * pxAddress, 116 BaseType_t xOpcode, 117 const uint8_t * const pucOptionsArray, 118 size_t * pxOptionsArraySize, 119 const NetworkEndPoint_t * pxEndPoint ); 120 121 /* 122 * Create the DHCP socket, if it has not been created already. 123 */ 124 _static void prvCreateDHCPSocket( const NetworkEndPoint_t * pxEndPoint ); 125 126 /* 127 * Close the DHCP socket, only when not in use anymore (i.e. xDHCPSocketUserCount = 0). 128 */ 129 static void prvCloseDHCPSocket( const NetworkEndPoint_t * pxEndPoint ); 130 131 static void vDHCPProcessEndPoint( BaseType_t xReset, 132 BaseType_t xDoCheck, 133 NetworkEndPoint_t * pxEndPoint ); 134 135 static BaseType_t xHandleWaitingOffer( NetworkEndPoint_t * pxEndPoint, 136 BaseType_t xDoCheck ); 137 138 static void vHandleWaitingAcknowledge( NetworkEndPoint_t * pxEndPoint, 139 BaseType_t xDoCheck ); 140 141 static BaseType_t xHandleWaitingFirstDiscover( NetworkEndPoint_t * pxEndPoint ); 142 143 static void prvHandleWaitingeLeasedAddress( NetworkEndPoint_t * pxEndPoint ); 144 145 static void vProcessHandleOption( NetworkEndPoint_t * pxEndPoint, 146 ProcessSet_t * pxSet, 147 BaseType_t xExpectedMessageType ); 148 static BaseType_t xProcessCheckOption( ProcessSet_t * pxSet ); 149 150 151 /*-----------------------------------------------------------*/ 152 153 /** 154 * @brief Check whether a given socket is the DHCP socket or not. 155 * 156 * @param[in] xSocket The socket to be checked. 157 * 158 * @return If the socket given as parameter is the DHCP socket - return 159 * pdTRUE, else pdFALSE. 160 */ xIsDHCPSocket(const ConstSocket_t xSocket)161 BaseType_t xIsDHCPSocket( const ConstSocket_t xSocket ) 162 { 163 BaseType_t xReturn; 164 165 if( xDHCPv4Socket == xSocket ) 166 { 167 xReturn = pdTRUE; 168 } 169 else 170 { 171 xReturn = pdFALSE; 172 } 173 174 return xReturn; 175 } 176 /*-----------------------------------------------------------*/ 177 178 /** 179 * @brief Returns the current state of a DHCP process. 180 * 181 * @param[in] pxEndPoint the end-point which is going through the DHCP process. 182 */ eGetDHCPState(const struct xNetworkEndPoint * pxEndPoint)183 eDHCPState_t eGetDHCPState( const struct xNetworkEndPoint * pxEndPoint ) 184 { 185 /* Note that EP_DHCPData is defined as "pxEndPoint->xDHCPData". */ 186 return EP_DHCPData.eDHCPState; 187 } 188 /*-----------------------------------------------------------*/ 189 190 /** 191 * @brief Process the DHCP state machine based on current state. 192 * 193 * @param[in] xReset Is the DHCP state machine starting over? pdTRUE/pdFALSE. 194 * @param[in] pxEndPoint The end-point for which the DHCP state machine should 195 * make one cycle. 196 */ vDHCPProcess(BaseType_t xReset,struct xNetworkEndPoint * pxEndPoint)197 void vDHCPProcess( BaseType_t xReset, 198 struct xNetworkEndPoint * pxEndPoint ) 199 { 200 BaseType_t xDoProcess = pdTRUE; 201 202 /* The function is called by the IP-task, so pxEndPoint 203 * should be non-NULL. */ 204 configASSERT( pxEndPoint != NULL ); 205 configASSERT( pxEndPoint->bits.bIPv6 == 0 ); 206 207 /* Is DHCP starting over? */ 208 if( xReset != pdFALSE ) 209 { 210 EP_DHCPData.eDHCPState = eInitialWait; 211 } 212 213 if( ( EP_DHCPData.eDHCPState != EP_DHCPData.eExpectedState ) && ( xReset == pdFALSE ) ) 214 { 215 /* When the DHCP event was generated, the DHCP client was 216 * in a different state. Therefore, ignore this event. */ 217 FreeRTOS_debug_printf( ( "DHCP wrong state: expect: %d got: %d : ignore\n", 218 EP_DHCPData.eExpectedState, EP_DHCPData.eDHCPState ) ); 219 } 220 else if( xDHCPv4Socket != NULL ) /* If there is a socket, check for incoming messages first. */ 221 { 222 /* No need to initialise 'pucUDPPayload', it just looks nicer. */ 223 uint8_t * pucUDPPayload = NULL; 224 const DHCPMessage_IPv4_t * pxDHCPMessage; 225 int32_t lBytes; 226 227 while( xDHCPv4Socket != NULL ) 228 { 229 BaseType_t xRecvFlags = FREERTOS_ZERO_COPY + FREERTOS_MSG_PEEK; 230 NetworkEndPoint_t * pxIterator = NULL; 231 232 /* Peek the next UDP message. */ 233 lBytes = FreeRTOS_recvfrom( xDHCPv4Socket, &( pucUDPPayload ), 0, xRecvFlags, NULL, NULL ); 234 235 if( lBytes < ( ( int32_t ) sizeof( DHCPMessage_IPv4_t ) ) ) 236 { 237 if( ( lBytes < 0 ) && ( lBytes != -pdFREERTOS_ERRNO_EAGAIN ) ) 238 { 239 FreeRTOS_printf( ( "vDHCPProcess: FreeRTOS_recvfrom returns %d\n", ( int ) lBytes ) ); 240 } 241 242 break; 243 } 244 245 /* Map a DHCP structure onto the received data. */ 246 /* MISRA Ref 11.3.1 [Misaligned access] */ 247 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 248 /* coverity[misra_c_2012_rule_11_3_violation] */ 249 pxDHCPMessage = ( ( const DHCPMessage_IPv4_t * ) pucUDPPayload ); 250 251 /* Sanity check. */ 252 if( ( pxDHCPMessage->ulDHCPCookie == dhcpCOOKIE ) && ( pxDHCPMessage->ucOpcode == dhcpREPLY_OPCODE ) ) 253 { 254 pxIterator = pxNetworkEndPoints; 255 256 /* Find the end-point with given transaction ID. */ 257 while( pxIterator != NULL ) 258 { 259 if( pxDHCPMessage->ulTransactionID == FreeRTOS_htonl( pxIterator->xDHCPData.ulTransactionId ) ) 260 { 261 break; 262 } 263 264 pxIterator = pxIterator->pxNext; 265 } 266 } 267 268 if( ( pxIterator != NULL ) && ( pxIterator->xDHCPData.eDHCPState == eLeasedAddress ) ) 269 { 270 /* No DHCP messages are expected while in eLeasedAddress state. */ 271 pxIterator = NULL; 272 } 273 274 if( pxIterator != NULL ) 275 { 276 /* The second parameter pdTRUE tells to check for a UDP message. */ 277 vDHCPProcessEndPoint( pdFALSE, pdTRUE, pxIterator ); 278 279 if( pxEndPoint == pxIterator ) 280 { 281 xDoProcess = pdFALSE; 282 } 283 } 284 else 285 { 286 /* Target not found, fetch the message and delete it. */ 287 /* PAss the address of a pointer pucUDPPayload, because zero-copy is used. */ 288 lBytes = FreeRTOS_recvfrom( xDHCPv4Socket, &( pucUDPPayload ), 0, FREERTOS_ZERO_COPY, NULL, NULL ); 289 290 if( ( lBytes > 0 ) && ( pucUDPPayload != NULL ) ) 291 { 292 /* Remove it now, destination not found. */ 293 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayload ); 294 FreeRTOS_printf( ( "vDHCPProcess: Removed a %d-byte message: target not found\n", ( int ) lBytes ) ); 295 } 296 } 297 } 298 } 299 else 300 { 301 /* do nothing, coverity happy */ 302 } 303 304 if( xDoProcess != pdFALSE ) 305 { 306 /* Process the end-point, but do not expect incoming packets. */ 307 vDHCPProcessEndPoint( xReset, pdFALSE, pxEndPoint ); 308 } 309 } 310 311 /** 312 * @brief Called by vDHCPProcessEndPoint(), this function handles the state 'eWaitingOffer'. 313 * If there is a reply, it will be examined, if there is a time-out, there may be a new 314 * new attempt, or it will give up. 315 * @param[in] pxEndPoint The end-point that is getting an IP-address from a DHCP server 316 * @param[in] xDoCheck When true, the function must handle any replies. 317 * @return It returns pdTRUE in case the DHCP process must be given up. 318 */ xHandleWaitingOffer(NetworkEndPoint_t * pxEndPoint,BaseType_t xDoCheck)319 static BaseType_t xHandleWaitingOffer( NetworkEndPoint_t * pxEndPoint, 320 BaseType_t xDoCheck ) 321 { 322 BaseType_t xGivingUp = pdFALSE; 323 324 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 325 eDHCPCallbackAnswer_t eAnswer; 326 #if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 ) 327 IP_Address_t xIPAddress; 328 #endif 329 #endif 330 331 /* Look for offers coming in. */ 332 if( xDoCheck != pdFALSE ) 333 { 334 if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER, pxEndPoint ) == pdPASS ) 335 { 336 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 337 /* Ask the user if a DHCP request is required. */ 338 #if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) 339 eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, EP_DHCPData.ulOfferedIPAddress ); 340 #else /* ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) */ 341 xIPAddress.ulIP_IPv4 = EP_DHCPData.ulOfferedIPAddress; 342 eAnswer = xApplicationDHCPHook_Multi( eDHCPPhasePreRequest, pxEndPoint, &xIPAddress ); 343 #endif /* ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) */ 344 345 if( eAnswer == eDHCPContinue ) 346 #endif /* ipconfigUSE_DHCP_HOOK */ 347 { 348 /* An offer has been made, the user wants to continue, 349 * generate the request. */ 350 if( prvSendDHCPRequest( pxEndPoint ) == pdPASS ) 351 { 352 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 353 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; 354 EP_DHCPData.eDHCPState = eWaitingAcknowledge; 355 } 356 else 357 { 358 /* Either the creation of a message buffer failed, or sendto(). 359 * Try again in the next cycle. */ 360 FreeRTOS_debug_printf( ( "Send failed during eWaitingOffer/1.\n" ) ); 361 EP_DHCPData.eDHCPState = eSendDHCPRequest; 362 } 363 } 364 365 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 366 else 367 { 368 if( eAnswer == eDHCPUseDefaults ) 369 { 370 ( void ) memcpy( &( pxEndPoint->ipv4_settings ), &( pxEndPoint->ipv4_defaults ), sizeof( pxEndPoint->ipv4_settings ) ); 371 } 372 373 /* The user indicates that the DHCP process does not continue. */ 374 xGivingUp = pdTRUE; 375 } 376 #endif /* ipconfigUSE_DHCP_HOOK */ 377 } 378 } 379 380 /* Is it time to send another Discover? */ 381 else if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) 382 { 383 /* It is time to send another Discover. Increase the time 384 * period, and if it has not got to the point of giving up - send 385 * another discovery. */ 386 EP_DHCPData.xDHCPTxPeriod <<= 1; 387 388 if( EP_DHCPData.xDHCPTxPeriod <= ( ( TickType_t ) ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) ) 389 { 390 if( xApplicationGetRandomNumber( &( EP_DHCPData.ulTransactionId ) ) != pdFALSE ) 391 { 392 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 393 394 if( EP_DHCPData.xUseBroadcast != pdFALSE ) 395 { 396 EP_DHCPData.xUseBroadcast = pdFALSE; 397 } 398 else 399 { 400 EP_DHCPData.xUseBroadcast = pdTRUE; 401 } 402 403 if( prvSendDHCPDiscover( pxEndPoint ) == pdPASS ) 404 { 405 FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", EP_DHCPData.xDHCPTxPeriod ) ); 406 } 407 else 408 { 409 /* Either the creation of a message buffer failed, or sendto(). 410 * Try again in the next cycle. */ 411 FreeRTOS_debug_printf( ( "Send failed during eWaitingOffer/2.\n" ) ); 412 EP_DHCPData.eDHCPState = eInitialWait; 413 } 414 } 415 else 416 { 417 FreeRTOS_debug_printf( ( "vDHCPProcess: failed to generate a random Transaction ID\n" ) ); 418 } 419 } 420 else 421 { 422 FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", EP_DHCPData.xDHCPTxPeriod, ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) ); 423 424 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 425 { 426 /* Only use a fake Ack if the default IP address == 0x00 427 * and the link local addressing is used. Start searching 428 * a free LinkLayer IP-address. Next state will be 429 * 'eGetLinkLayerAddress'. */ 430 prvPrepareLinkLayerIPLookUp( pxEndPoint ); 431 432 /* Setting an IP address manually so set to not using 433 * leased address mode. */ 434 EP_DHCPData.eDHCPState = eGetLinkLayerAddress; 435 } 436 #else 437 { 438 xGivingUp = pdTRUE; 439 } 440 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ 441 } 442 } 443 else 444 { 445 /* There was no DHCP reply, there was no time-out, just keep on waiting. */ 446 } 447 448 return xGivingUp; 449 } 450 /*-----------------------------------------------------------*/ 451 452 /** 453 * @brief Called by vDHCPProcessEndPoint(), this function handles the state 'eWaitingAcknowledge'. 454 * If there is a reply, it will be examined, if there is a time-out, there may be a new 455 * new attempt, or it will give up. 456 * After the acknowledge, the leasing of an IP-address will start. 457 * @param[in] pxEndPoint The end-point that is getting an IP-address from a DHCP server 458 * @param[in] xDoCheck When true, the function must handle any replies. 459 */ vHandleWaitingAcknowledge(NetworkEndPoint_t * pxEndPoint,BaseType_t xDoCheck)460 static void vHandleWaitingAcknowledge( NetworkEndPoint_t * pxEndPoint, 461 BaseType_t xDoCheck ) 462 { 463 if( xDoCheck == pdFALSE ) 464 { 465 /* Is it time to send another Discover? */ 466 if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) 467 { 468 /* Increase the time period, and if it has not got to the 469 * point of giving up - send another request. */ 470 EP_DHCPData.xDHCPTxPeriod <<= 1; 471 472 if( EP_DHCPData.xDHCPTxPeriod <= ( TickType_t ) ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) 473 { 474 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 475 476 if( prvSendDHCPRequest( pxEndPoint ) == pdPASS ) 477 { 478 /* The message is sent. Stay in state 'eWaitingAcknowledge'. */ 479 } 480 else 481 { 482 /* Either the creation of a message buffer failed, or sendto(). 483 * Try again in the next cycle. */ 484 FreeRTOS_debug_printf( ( "Send failed during eWaitingAcknowledge.\n" ) ); 485 EP_DHCPData.eDHCPState = eSendDHCPRequest; 486 } 487 } 488 else 489 { 490 /* Give up, start again. */ 491 EP_DHCPData.eDHCPState = eInitialWait; 492 } 493 } 494 } 495 else if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK, pxEndPoint ) == pdPASS ) 496 { 497 FreeRTOS_debug_printf( ( "vDHCPProcess: acked %xip\n", ( unsigned int ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) ); 498 499 /* DHCP completed. The IP address can now be used, and the 500 * timer set to the lease timeout time. */ 501 EP_IPv4_SETTINGS.ulIPAddress = EP_DHCPData.ulOfferedIPAddress; 502 503 /* Setting the 'local' broadcast address, something like 504 * '192.168.1.255'. */ 505 EP_IPv4_SETTINGS.ulBroadcastAddress = EP_DHCPData.ulOfferedIPAddress | ~( EP_IPv4_SETTINGS.ulNetMask ); 506 EP_DHCPData.eDHCPState = eLeasedAddress; 507 508 /* _HT_ This macro must be removed later. 509 * It is enough to set 'EP_IPv4_SETTINGS.ulIPAddress'. */ 510 *ipLOCAL_IP_ADDRESS_POINTER = EP_IPv4_SETTINGS.ulIPAddress; 511 512 iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress ); 513 514 /* DHCP failed, the default configured IP-address will be used 515 * Now call vIPNetworkUpCalls() to send the network-up event and 516 * start the ARP timer. */ 517 vIPNetworkUpCalls( pxEndPoint ); 518 /* Close socket to ensure packets don't queue on it. */ 519 prvCloseDHCPSocket( pxEndPoint ); 520 521 if( EP_DHCPData.ulLeaseTime == 0U ) 522 { 523 EP_DHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; 524 } 525 else if( EP_DHCPData.ulLeaseTime < dhcpMINIMUM_LEASE_TIME ) 526 { 527 EP_DHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME; 528 } 529 else 530 { 531 /* The lease time is already valid. */ 532 } 533 534 /* Check for clashes. */ 535 vARPSendGratuitous(); 536 vDHCP_RATimerReload( ( struct xNetworkEndPoint * ) pxEndPoint, EP_DHCPData.ulLeaseTime ); 537 } 538 else 539 { 540 /* There are no replies yet. */ 541 } 542 } 543 544 /** 545 * @brief Called by vDHCPProcessEndPoint(), this function handles the state 'eWaitingSendFirstDiscover'. 546 * If will send a DISCOVER message to a DHCP server, and move to the next status 'eWaitingOffer'. 547 * @param[in] pxEndPoint The end-point that is getting an IP-address from a DHCP server 548 * @return xGivingUp: when pdTRUE, there was a fatal error and the process can not continue; 549 */ xHandleWaitingFirstDiscover(NetworkEndPoint_t * pxEndPoint)550 static BaseType_t xHandleWaitingFirstDiscover( NetworkEndPoint_t * pxEndPoint ) 551 { 552 BaseType_t xGivingUp = pdFALSE; 553 554 /* Ask the user if a DHCP discovery is required. */ 555 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 556 #if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) 557 eDHCPCallbackAnswer_t eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, pxEndPoint->ipv4_defaults.ulIPAddress ); 558 #else /* ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) */ 559 IP_Address_t xIPAddress; 560 eDHCPCallbackAnswer_t eAnswer; 561 562 xIPAddress.ulIP_IPv4 = pxEndPoint->ipv4_defaults.ulIPAddress; 563 eAnswer = xApplicationDHCPHook_Multi( eDHCPPhasePreDiscover, pxEndPoint, &xIPAddress ); 564 #endif /* ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) */ 565 566 if( eAnswer == eDHCPContinue ) 567 #endif /* ipconfigUSE_DHCP_HOOK */ 568 { 569 /* See if prvInitialiseDHCP() has creates a socket. */ 570 if( xDHCPv4Socket == NULL ) 571 { 572 xGivingUp = pdTRUE; 573 } 574 else 575 { 576 /* Put 'ulIPAddress' to zero to indicate that the end-point is down. */ 577 EP_IPv4_SETTINGS.ulIPAddress = 0U; 578 579 /* Send the first discover request. */ 580 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 581 582 if( prvSendDHCPDiscover( pxEndPoint ) == pdPASS ) 583 { 584 EP_DHCPData.eDHCPState = eWaitingOffer; 585 } 586 else 587 { 588 /* Either the creation of a message buffer failed, or sendto(). 589 * Try again in the next cycle. */ 590 FreeRTOS_debug_printf( ( "Send failed during eWaitingSendFirstDiscover\n" ) ); 591 } 592 } 593 } 594 595 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 596 else 597 { 598 if( eAnswer == eDHCPUseDefaults ) 599 { 600 ( void ) memcpy( &( pxEndPoint->ipv4_settings ), &( pxEndPoint->ipv4_defaults ), sizeof( pxEndPoint->ipv4_settings ) ); 601 } 602 603 /* The user indicates that the DHCP process does not continue. */ 604 xGivingUp = pdTRUE; 605 } 606 #endif /* ipconfigUSE_DHCP_HOOK */ 607 608 return xGivingUp; 609 } 610 /*-----------------------------------------------------------*/ 611 612 /** 613 * @brief Called by vDHCPProcessEndPoint(), this function handles the state 'eLeasedAddress'. 614 * If waits until the lease must be renewed, and then send a new request. 615 * @param[in] pxEndPoint The end-point that is getting an IP-address from a DHCP server 616 */ prvHandleWaitingeLeasedAddress(NetworkEndPoint_t * pxEndPoint)617 static void prvHandleWaitingeLeasedAddress( NetworkEndPoint_t * pxEndPoint ) 618 { 619 if( FreeRTOS_IsEndPointUp( pxEndPoint ) != 0 ) 620 { 621 /* Resend the request at the appropriate time to renew the lease. */ 622 prvCreateDHCPSocket( pxEndPoint ); 623 624 if( xDHCPv4Socket != NULL ) 625 { 626 uint32_t ulID = 0U; 627 628 if( xApplicationGetRandomNumber( &( ulID ) ) != pdFALSE ) 629 { 630 EP_DHCPData.ulTransactionId = ulID; 631 } 632 633 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 634 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; 635 636 if( prvSendDHCPRequest( pxEndPoint ) == pdPASS ) 637 { 638 /* The packet was sent successfully, wait for an acknowledgement. */ 639 EP_DHCPData.eDHCPState = eWaitingAcknowledge; 640 } 641 else 642 { 643 /* The packet was not sent, try sending it later. */ 644 EP_DHCPData.eDHCPState = eSendDHCPRequest; 645 FreeRTOS_debug_printf( ( "Send failed eLeasedAddress.\n" ) ); 646 } 647 648 /* From now on, we should be called more often */ 649 vDHCP_RATimerReload( pxEndPoint, dhcpINITIAL_TIMER_PERIOD ); 650 } 651 } 652 else 653 { 654 /* See PR #53 on github/freertos/freertos */ 655 FreeRTOS_printf( ( "DHCP: lease time finished but network is down\n" ) ); 656 vDHCP_RATimerReload( ( struct xNetworkEndPoint * ) pxEndPoint, pdMS_TO_TICKS( 5000U ) ); 657 } 658 } 659 /*-----------------------------------------------------------*/ 660 661 /** 662 * @brief Process the DHCP state machine based on current state. 663 * 664 * @param[in] xReset Is the DHCP state machine starting over? pdTRUE/pdFALSE. 665 * @param[in] xDoCheck true when an incoming message is to be expected, and 666 * prvProcessDHCPReplies() will be called. 667 * @param[in] pxEndPoint The end-point for which the DHCP state machine should 668 * make one cycle. 669 */ vDHCPProcessEndPoint(BaseType_t xReset,BaseType_t xDoCheck,NetworkEndPoint_t * pxEndPoint)670 static void vDHCPProcessEndPoint( BaseType_t xReset, 671 BaseType_t xDoCheck, 672 NetworkEndPoint_t * pxEndPoint ) 673 { 674 BaseType_t xGivingUp = pdFALSE; 675 676 configASSERT( pxEndPoint != NULL ); 677 678 /* Is DHCP starting over? */ 679 if( xReset != pdFALSE ) 680 { 681 EP_DHCPData.eDHCPState = eInitialWait; 682 } 683 684 if( ( EP_DHCPData.eDHCPState != EP_DHCPData.eExpectedState ) && ( xReset == pdFALSE ) ) 685 { 686 /* When the DHCP event was generated, the DHCP client was 687 * in a different state. Therefore, ignore this event. */ 688 FreeRTOS_debug_printf( ( "vDHCPProcessEndPoint: wrong state: expect: %d got: %d : ignore\n", 689 EP_DHCPData.eExpectedState, EP_DHCPData.eDHCPState ) ); 690 } 691 else 692 { 693 { 694 static eDHCPState_t eLastState = eNotUsingLeasedAddress; 695 696 if( eLastState != EP_DHCPData.eDHCPState ) 697 { 698 eLastState = EP_DHCPData.eDHCPState; 699 FreeRTOS_debug_printf( ( "vDHCPProcessEndPoint: enter %d\n", EP_DHCPData.eDHCPState ) ); 700 } 701 } 702 703 switch( EP_DHCPData.eDHCPState ) 704 { 705 case eInitialWait: 706 707 /* Initial state. Create the DHCP socket, timer, etc. if they 708 * have not already been created. */ 709 710 /* Initial state. Create the DHCP socket, timer, etc. if they 711 * have not already been created. */ 712 prvInitialiseDHCP( pxEndPoint ); 713 EP_DHCPData.eDHCPState = eWaitingSendFirstDiscover; 714 break; 715 716 case eWaitingSendFirstDiscover: 717 xGivingUp = xHandleWaitingFirstDiscover( pxEndPoint ); 718 break; 719 720 case eSendDHCPRequest: 721 722 if( prvSendDHCPRequest( pxEndPoint ) == pdPASS ) 723 { 724 /* Send succeeded, go to state 'eWaitingAcknowledge'. */ 725 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 726 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; 727 EP_DHCPData.eDHCPState = eWaitingAcknowledge; 728 } 729 else 730 { 731 /* Either the creation of a message buffer failed, or sendto(). 732 * Try again in the next cycle. */ 733 FreeRTOS_debug_printf( ( "Send failed during eSendDHCPRequest.\n" ) ); 734 } 735 736 break; 737 738 case eWaitingOffer: 739 xGivingUp = xHandleWaitingOffer( pxEndPoint, xDoCheck ); 740 break; 741 742 case eWaitingAcknowledge: 743 vHandleWaitingAcknowledge( pxEndPoint, xDoCheck ); 744 break; 745 746 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 747 case eGetLinkLayerAddress: 748 749 if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) 750 { 751 if( xARPHadIPClash == pdFALSE ) 752 { 753 /* ARP OK. proceed. */ 754 iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress ); 755 756 /* Auto-IP succeeded, the default configured IP-address will 757 * be used. Now call vIPNetworkUpCalls() to send the 758 * network-up event and start the ARP timer. */ 759 vIPNetworkUpCalls( pxEndPoint ); 760 761 EP_DHCPData.eDHCPState = eNotUsingLeasedAddress; 762 } 763 else 764 { 765 /* ARP clashed - try another IP address. */ 766 prvPrepareLinkLayerIPLookUp( pxEndPoint ); 767 768 /* Setting an IP address manually so set to not using leased 769 * address mode. */ 770 EP_DHCPData.eDHCPState = eGetLinkLayerAddress; 771 } 772 } 773 break; 774 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ 775 776 case eLeasedAddress: 777 prvHandleWaitingeLeasedAddress( pxEndPoint ); 778 break; 779 780 case eNotUsingLeasedAddress: 781 782 vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE ); 783 break; 784 785 default: 786 /* Lint: all options are included. */ 787 break; 788 } 789 790 { 791 static eDHCPState_t eLastState = eNotUsingLeasedAddress; 792 793 if( eLastState != EP_DHCPData.eDHCPState ) 794 { 795 eLastState = EP_DHCPData.eDHCPState; 796 FreeRTOS_debug_printf( ( "vDHCPProcessEndPoint: exit %d\n", EP_DHCPData.eDHCPState ) ); 797 } 798 } 799 800 if( xGivingUp != pdFALSE ) 801 { 802 /* xGivingUp became true either because of a time-out, or because 803 * xApplicationDHCPHook() returned another value than 'eDHCPContinue', 804 * meaning that the conversion is cancelled from here. */ 805 806 /* Revert to static IP address. */ 807 taskENTER_CRITICAL(); 808 { 809 EP_IPv4_SETTINGS.ulIPAddress = pxEndPoint->ipv4_defaults.ulIPAddress; 810 iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( pxEndPoint->ipv4_defaults.ulIPAddress ); 811 } 812 taskEXIT_CRITICAL(); 813 814 EP_DHCPData.eDHCPState = eNotUsingLeasedAddress; 815 vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE ); 816 817 /* DHCP failed, the default configured IP-address will be used. Now 818 * call vIPNetworkUpCalls() to send the network-up event and start the ARP 819 * timer. */ 820 821 vIPNetworkUpCalls( pxEndPoint ); 822 823 /* Close socket to ensure packets don't queue on it. */ 824 prvCloseDHCPSocket( pxEndPoint ); 825 } 826 } 827 } 828 /*-----------------------------------------------------------*/ 829 830 /** 831 * @brief Close the DHCP socket, but only when there are no other end-points 832 * using it. 833 * @param[in] pxEndPoint The end-point that stops using the socket. 834 */ prvCloseDHCPSocket(const NetworkEndPoint_t * pxEndPoint)835 static void prvCloseDHCPSocket( const NetworkEndPoint_t * pxEndPoint ) 836 { 837 ( void ) pxEndPoint; 838 839 if( ( xDHCPv4Socket != NULL ) && ( xDHCPSocketUserCount > 0 ) ) 840 { 841 xDHCPSocketUserCount--; 842 843 if( xDHCPSocketUserCount == 0 ) 844 { 845 /* This modules runs from the IP-task. Use the internal 846 * function 'vSocketClose()` to close the socket. */ 847 ( void ) vSocketClose( xDHCPv4Socket ); 848 xDHCPv4Socket = NULL; 849 } 850 } 851 else 852 { 853 /* Strange: there is a socket, but there are no users. */ 854 } 855 856 FreeRTOS_printf( ( "prvCloseDHCPSocket[%02x-%02x]: %s, user count %d\n", 857 pxEndPoint->xMACAddress.ucBytes[ 4 ], 858 pxEndPoint->xMACAddress.ucBytes[ 5 ], 859 ( xDHCPv4Socket != NULL ) ? "open" : "closed", 860 ( int ) xDHCPSocketUserCount ) ); 861 } 862 /*-----------------------------------------------------------*/ 863 864 /** 865 * @brief Create a DHCP socket with the defined timeouts. The same socket 866 * will be shared among all end-points that need DHCP. 867 */ prvCreateDHCPSocket(const NetworkEndPoint_t * pxEndPoint)868 _static void prvCreateDHCPSocket( const NetworkEndPoint_t * pxEndPoint ) 869 { 870 struct freertos_sockaddr xAddress; 871 BaseType_t xReturn; 872 TickType_t xTimeoutTime = ( TickType_t ) 0; 873 874 if( xDHCPv4Socket == NULL ) /* Create the socket, if it has not already been created. */ 875 { 876 xDHCPv4Socket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); 877 configASSERT( xSocketValid( xDHCPv4Socket ) == pdTRUE ); 878 879 /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ 880 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ 881 /* coverity[misra_c_2012_rule_11_4_violation] */ 882 if( xSocketValid( xDHCPv4Socket ) == pdTRUE ) 883 { 884 /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the 885 * context of the IP task. */ 886 ( void ) FreeRTOS_setsockopt( xDHCPv4Socket, 0, FREERTOS_SO_RCVTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) ); 887 ( void ) FreeRTOS_setsockopt( xDHCPv4Socket, 0, FREERTOS_SO_SNDTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) ); 888 889 /* Bind to the standard DHCP client port. */ 890 xAddress.sin_port = ( uint16_t ) dhcpCLIENT_PORT_IPv4; 891 xReturn = vSocketBind( xDHCPv4Socket, &xAddress, sizeof( xAddress ), pdFALSE ); 892 xDHCPSocketUserCount = 1; 893 FreeRTOS_printf( ( "DHCP-socket[%02x-%02x]: DHCP Socket Create\n", 894 pxEndPoint->xMACAddress.ucBytes[ 4 ], 895 pxEndPoint->xMACAddress.ucBytes[ 5 ] ) ); 896 897 if( xReturn != 0 ) 898 { 899 /* Binding failed, close the socket again. */ 900 prvCloseDHCPSocket( pxEndPoint ); 901 } 902 } 903 else 904 { 905 /* Change to NULL for easier testing. */ 906 xDHCPv4Socket = NULL; 907 } 908 } 909 else 910 { 911 xDHCPSocketUserCount++; 912 } 913 914 FreeRTOS_printf( ( "prvCreateDHCPSocket[%02x-%02x]: %s, user count %d\n", 915 pxEndPoint->xMACAddress.ucBytes[ 4 ], 916 pxEndPoint->xMACAddress.ucBytes[ 5 ], 917 ( xDHCPv4Socket != NULL ) ? "open" : "closed", 918 ( int ) xDHCPSocketUserCount ) ); 919 } 920 /*-----------------------------------------------------------*/ 921 922 /** 923 * @brief Initialise the DHCP state machine by creating DHCP socket and 924 * begin the transaction. 925 * 926 * @param[in] pxEndPoint The end-point that needs DHCP. 927 */ prvInitialiseDHCP(NetworkEndPoint_t * pxEndPoint)928 static void prvInitialiseDHCP( NetworkEndPoint_t * pxEndPoint ) 929 { 930 /* Initialise the parameters that will be set by the DHCP process. Per 931 * https://www.ietf.org/rfc/rfc2131.txt, Transaction ID should be a random 932 * value chosen by the client. */ 933 934 /* Check for random number generator API failure. */ 935 if( xApplicationGetRandomNumber( &( EP_DHCPData.ulTransactionId ) ) != pdFALSE ) 936 { 937 EP_DHCPData.xUseBroadcast = 0; 938 EP_DHCPData.ulOfferedIPAddress = 0U; 939 EP_DHCPData.ulDHCPServerAddress = 0U; 940 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; 941 942 /* Create the DHCP socket if it has not already been created. */ 943 prvCreateDHCPSocket( pxEndPoint ); 944 FreeRTOS_debug_printf( ( "prvInitialiseDHCP: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) ); 945 vDHCP_RATimerReload( pxEndPoint, dhcpINITIAL_TIMER_PERIOD ); 946 } 947 else 948 { 949 FreeRTOS_debug_printf( ( "prvInitialiseDHCP: failed to generate a random Transaction ID\n" ) ); 950 } 951 } 952 /*-----------------------------------------------------------*/ 953 954 /** 955 * @brief Called by prvProcessDHCPReplies(), which walks through an array of DHCP options, 956 * this function will check a single option. 957 * @param[in] pxEndPoint The end-point that needs an IP-address. 958 * @param[in] pxSet A set of variables that describe the parsing process. 959 * @param[in] xExpectedMessageType The type of message expected in the 960 * dhcpIPv4_MESSAGE_TYPE_OPTION_CODE option. 961 */ vProcessHandleOption(NetworkEndPoint_t * pxEndPoint,ProcessSet_t * pxSet,BaseType_t xExpectedMessageType)962 static void vProcessHandleOption( NetworkEndPoint_t * pxEndPoint, 963 ProcessSet_t * pxSet, 964 BaseType_t xExpectedMessageType ) 965 { 966 /* Option-specific handling. */ 967 968 switch( pxSet->ucOptionCode ) 969 { 970 case dhcpIPv4_MESSAGE_TYPE_OPTION_CODE: 971 972 if( pxSet->pucByte[ pxSet->uxIndex ] == ( uint8_t ) xExpectedMessageType ) 973 { 974 /* The message type is the message type the 975 * state machine is expecting. */ 976 pxSet->ulProcessed++; 977 } 978 else 979 { 980 if( pxSet->pucByte[ pxSet->uxIndex ] == ( uint8_t ) dhcpMESSAGE_TYPE_NACK ) 981 { 982 if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_ACK ) 983 { 984 /* Start again. */ 985 EP_DHCPData.eDHCPState = eInitialWait; 986 } 987 } 988 989 /* Stop processing further options. */ 990 pxSet->uxLength = 0; 991 } 992 993 break; 994 995 case dhcpIPv4_SUBNET_MASK_OPTION_CODE: 996 997 if( pxSet->uxLength == sizeof( uint32_t ) ) 998 { 999 EP_IPv4_SETTINGS.ulNetMask = pxSet->ulParameter; 1000 } 1001 1002 break; 1003 1004 case dhcpIPv4_GATEWAY_OPTION_CODE: 1005 1006 /* The DHCP server may send more than 1 gateway addresses. */ 1007 if( pxSet->uxLength >= sizeof( uint32_t ) ) 1008 { 1009 /* ulProcessed is not incremented in this case 1010 * because the gateway is not essential. */ 1011 EP_IPv4_SETTINGS.ulGatewayAddress = pxSet->ulParameter; 1012 } 1013 1014 break; 1015 1016 case dhcpIPv4_DNS_SERVER_OPTIONS_CODE: 1017 1018 /* ulProcessed is not incremented in this case 1019 * because the DNS server is not essential. Only the 1020 * first DNS server address is taken. */ 1021 if( pxSet->uxLength >= sizeof( uint32_t ) ) 1022 { 1023 size_t uxSourceIndex; 1024 size_t uxTargetIndex = 0; 1025 size_t uxDNSCount = pxSet->uxLength / sizeof( uint32_t ); 1026 size_t uxByteIndex = pxSet->uxIndex; 1027 1028 void * pvCopyDest = &( pxSet->ulParameter ); 1029 1030 /* Just to try-out for CBMC. */ 1031 if( uxDNSCount > ipconfigENDPOINT_DNS_ADDRESS_COUNT ) 1032 { 1033 uxDNSCount = ipconfigENDPOINT_DNS_ADDRESS_COUNT; 1034 } 1035 1036 for( uxSourceIndex = 0U; uxSourceIndex < uxDNSCount; uxSourceIndex++ ) 1037 { 1038 const void * pvCopySource = &( pxSet->pucByte[ uxByteIndex ] ); 1039 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxSet->ulParameter ) ); 1040 1041 if( ( pxSet->ulParameter != FREERTOS_INADDR_ANY ) && ( pxSet->ulParameter != ipBROADCAST_IP_ADDRESS ) ) 1042 { 1043 EP_IPv4_SETTINGS.ulDNSServerAddresses[ uxTargetIndex ] = pxSet->ulParameter; 1044 uxTargetIndex++; 1045 /* uxDNSCount <= ipconfigENDPOINT_DNS_ADDRESS_COUNT , hence check is removed. */ 1046 } 1047 1048 uxByteIndex += sizeof( uint32_t ); 1049 } 1050 1051 /* Clear the remaining entries. */ 1052 while( uxTargetIndex < ipconfigENDPOINT_DNS_ADDRESS_COUNT ) 1053 { 1054 EP_IPv4_SETTINGS.ulDNSServerAddresses[ uxTargetIndex ] = 0U; 1055 uxTargetIndex++; 1056 } 1057 1058 /* For the next lookup, start using the first DNS entry. */ 1059 EP_IPv4_SETTINGS.ucDNSIndex = 0U; 1060 } 1061 1062 break; 1063 1064 case dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE: 1065 1066 if( pxSet->uxLength == sizeof( uint32_t ) ) 1067 { 1068 if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_OFFER ) 1069 { 1070 /* Offers state the replying server. */ 1071 pxSet->ulProcessed++; 1072 EP_DHCPData.ulDHCPServerAddress = pxSet->ulParameter; 1073 } 1074 else 1075 { 1076 /* The ack must come from the expected server. */ 1077 if( EP_DHCPData.ulDHCPServerAddress == pxSet->ulParameter ) 1078 { 1079 pxSet->ulProcessed++; 1080 } 1081 } 1082 } 1083 1084 break; 1085 1086 case dhcpIPv4_LEASE_TIME_OPTION_CODE: 1087 1088 if( pxSet->uxLength == sizeof( EP_DHCPData.ulLeaseTime ) ) 1089 { 1090 /* ulProcessed is not incremented in this case 1091 * because the lease time is not essential. */ 1092 1093 /* The DHCP parameter is in seconds, convert 1094 * to host-endian format. */ 1095 EP_DHCPData.ulLeaseTime = FreeRTOS_ntohl( pxSet->ulParameter ); 1096 1097 /* Divide the lease time by two to ensure a renew 1098 * request is sent before the lease actually expires. */ 1099 EP_DHCPData.ulLeaseTime >>= 1U; 1100 1101 /* Multiply with configTICK_RATE_HZ to get clock ticks. */ 1102 EP_DHCPData.ulLeaseTime = ( uint32_t ) configTICK_RATE_HZ * ( uint32_t ) EP_DHCPData.ulLeaseTime; 1103 } 1104 1105 break; 1106 1107 default: 1108 1109 /* Not interested in this field. */ 1110 1111 break; 1112 } 1113 } 1114 /*-----------------------------------------------------------*/ 1115 1116 /** 1117 * @brief Check whether the DHCP response from the server has all valid 1118 * invariant parameters and valid (non broadcast and non localhost) 1119 * IP address being assigned to the device. 1120 * 1121 * @param[in] pxDHCPMessage The DHCP message. 1122 * 1123 * @return pdPASS if the DHCP response has correct parameters; pdFAIL otherwise. 1124 */ prvIsValidDHCPResponse(const DHCPMessage_IPv4_t * pxDHCPMessage)1125 static BaseType_t prvIsValidDHCPResponse( const DHCPMessage_IPv4_t * pxDHCPMessage ) 1126 { 1127 BaseType_t xReturn = pdPASS; 1128 1129 if( ( pxDHCPMessage->ulDHCPCookie != ( uint32_t ) dhcpCOOKIE ) || 1130 ( pxDHCPMessage->ucOpcode != ( uint8_t ) dhcpREPLY_OPCODE ) || 1131 ( pxDHCPMessage->ucAddressType != ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET ) || 1132 ( pxDHCPMessage->ucAddressLength != ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH ) || 1133 ( ( FreeRTOS_ntohl( pxDHCPMessage->ulYourIPAddress_yiaddr ) & 0xFFU ) == 0xFFU ) || 1134 ( ( ( pxDHCPMessage->ulYourIPAddress_yiaddr & 0x7FU ) ^ 0x7FU ) == 0x00U ) ) 1135 { 1136 /* Invalid cookie OR 1137 * Unexpected opcode OR 1138 * Incorrect address type OR 1139 * Incorrect address length OR 1140 * The DHCP server is trying to assign a broadcast address to the device OR 1141 * The DHCP server is trying to assign a localhost address to the device. */ 1142 xReturn = pdFAIL; 1143 } 1144 1145 return xReturn; 1146 } 1147 /*-----------------------------------------------------------*/ 1148 1149 /** 1150 * @brief Check an incoming DHCP option. 1151 * 1152 * @param[in] pxSet A set of variables needed to parse the DHCP reply. 1153 * 1154 * @return pdPASS: 1 when the option must be analysed, 0 when the option 1155 * must be skipped, and -1 when parsing must stop. 1156 */ xProcessCheckOption(ProcessSet_t * pxSet)1157 static BaseType_t xProcessCheckOption( ProcessSet_t * pxSet ) 1158 { 1159 BaseType_t xResult = -1; 1160 1161 do 1162 { 1163 if( pxSet->ucOptionCode == ( uint8_t ) dhcpOPTION_END_BYTE ) 1164 { 1165 /* Ready, the last byte has been seen. 1166 * Return -1 so that the parsing will stop. */ 1167 break; 1168 } 1169 1170 if( pxSet->ucOptionCode == ( uint8_t ) dhcpIPv4_ZERO_PAD_OPTION_CODE ) 1171 { 1172 /* The value zero is used as a pad byte, 1173 * it is not followed by a length byte. */ 1174 pxSet->uxIndex++; 1175 /* Return zero to skip this option. */ 1176 xResult = 0; 1177 break; 1178 } 1179 1180 /* Stop if the response is malformed. */ 1181 if( ( pxSet->uxIndex + 1U ) >= pxSet->uxPayloadDataLength ) 1182 { 1183 /* The length byte is missing, stop parsing. */ 1184 break; 1185 } 1186 1187 /* Fetch the length byte. */ 1188 pxSet->uxLength = ( size_t ) pxSet->pucByte[ pxSet->uxIndex + 1U ]; 1189 pxSet->uxIndex = pxSet->uxIndex + 2U; 1190 1191 if( !( ( ( pxSet->uxIndex + pxSet->uxLength ) - 1U ) < pxSet->uxPayloadDataLength ) ) 1192 { 1193 /* There are not as many bytes left as there should be. */ 1194 break; 1195 } 1196 1197 /* In most cases, a 4-byte network-endian parameter follows, 1198 * just get it once here and use later. */ 1199 if( pxSet->uxLength >= sizeof( pxSet->ulParameter ) ) 1200 { 1201 /* 1202 * Use helper variables for memcpy() to remain 1203 * compliant with MISRA Rule 21.15. These should be 1204 * optimized away. 1205 */ 1206 const void * pvCopySource = &( pxSet->pucByte[ pxSet->uxIndex ] ); 1207 void * pvCopyDest = &( pxSet->ulParameter ); 1208 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxSet->ulParameter ) ); 1209 /* 'uxIndex' will be increased at the end of this loop. */ 1210 } 1211 else 1212 { 1213 pxSet->ulParameter = 0; 1214 } 1215 1216 /* Confirm uxIndex is still a valid index after adjustments to uxIndex above */ 1217 if( !( pxSet->uxIndex < pxSet->uxPayloadDataLength ) ) 1218 { 1219 break; 1220 } 1221 1222 /* Return 1 so that the option will be processed. */ 1223 xResult = 1; 1224 /* Try to please CBMC with a break statement here. */ 1225 break; 1226 } while( ipFALSE_BOOL ); 1227 1228 return xResult; 1229 } 1230 /*-----------------------------------------------------------*/ 1231 1232 /** 1233 * @brief Process the DHCP replies. 1234 * 1235 * @param[in] xExpectedMessageType The type of the message the DHCP state machine is expecting. 1236 * Messages of different type will be dropped. 1237 * @param[in] pxEndPoint The end-point to whom the replies are addressed. 1238 * 1239 * @return pdPASS: if DHCP options are received correctly; pdFAIL: Otherwise. 1240 */ prvProcessDHCPReplies(BaseType_t xExpectedMessageType,NetworkEndPoint_t * pxEndPoint)1241 static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType, 1242 NetworkEndPoint_t * pxEndPoint ) 1243 { 1244 uint8_t * pucUDPPayload; 1245 int32_t lBytes; 1246 const DHCPMessage_IPv4_t * pxDHCPMessage; 1247 BaseType_t xReturn = pdFALSE; 1248 const uint32_t ulMandatoryOptions = 2U; /* DHCP server address, and the correct DHCP message type must be present in the options. */ 1249 ProcessSet_t xSet; 1250 1251 ( void ) memset( &( xSet ), 0, sizeof( xSet ) ); 1252 1253 /* Passing the address of a pointer (pucUDPPayload) because FREERTOS_ZERO_COPY is used. */ 1254 lBytes = FreeRTOS_recvfrom( xDHCPv4Socket, &pucUDPPayload, 0U, FREERTOS_ZERO_COPY, NULL, NULL ); 1255 1256 if( lBytes > 0 ) 1257 { 1258 /* Map a DHCP structure onto the received data. */ 1259 1260 /* MISRA Ref 11.3.1 [Misaligned access] */ 1261 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 1262 /* coverity[misra_c_2012_rule_11_3_violation] */ 1263 pxDHCPMessage = ( ( DHCPMessage_IPv4_t * ) pucUDPPayload ); 1264 1265 /* Sanity check. */ 1266 if( lBytes < ( int32_t ) sizeof( DHCPMessage_IPv4_t ) ) 1267 { 1268 /* Not enough bytes. */ 1269 } 1270 else if( prvIsValidDHCPResponse( pxDHCPMessage ) == pdFAIL ) 1271 { 1272 /* Invalid values in DHCP response. */ 1273 } 1274 else if( ( pxDHCPMessage->ulTransactionID != FreeRTOS_htonl( EP_DHCPData.ulTransactionId ) ) ) 1275 { 1276 /* Transaction ID does not match. */ 1277 } 1278 else /* Looks like a valid DHCP response, with the same transaction ID. */ 1279 { 1280 if( memcmp( pxDHCPMessage->ucClientHardwareAddress, 1281 pxEndPoint->xMACAddress.ucBytes, 1282 sizeof( MACAddress_t ) ) != 0 ) 1283 { 1284 /* Target MAC address doesn't match. */ 1285 } 1286 else 1287 { 1288 /* None of the essential options have been processed yet. */ 1289 xSet.ulProcessed = 0U; 1290 1291 /* Walk through the options until the dhcpOPTION_END_BYTE byte 1292 * is found, taking care not to walk off the end of the options. */ 1293 xSet.pucByte = &( pucUDPPayload[ sizeof( DHCPMessage_IPv4_t ) ] ); 1294 xSet.uxIndex = 0; 1295 xSet.uxPayloadDataLength = ( ( size_t ) lBytes ) - sizeof( DHCPMessage_IPv4_t ); 1296 1297 while( xSet.uxIndex < xSet.uxPayloadDataLength ) 1298 { 1299 BaseType_t xResult; 1300 xSet.ucOptionCode = xSet.pucByte[ xSet.uxIndex ]; 1301 1302 xResult = xProcessCheckOption( &( xSet ) ); 1303 1304 if( xResult > 0 ) 1305 { 1306 vProcessHandleOption( pxEndPoint, &( xSet ), xExpectedMessageType ); 1307 } 1308 1309 if( xResult != 0 ) 1310 { 1311 if( ( xSet.uxLength == 0U ) || ( xResult < 0 ) ) 1312 { 1313 break; 1314 } 1315 1316 xSet.uxIndex += xSet.uxLength; 1317 } 1318 } 1319 1320 /* Were all the mandatory options received? */ 1321 if( xSet.ulProcessed >= ulMandatoryOptions ) 1322 { 1323 /* HT:endian: used to be network order */ 1324 EP_DHCPData.ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr; 1325 FreeRTOS_printf( ( "vDHCPProcess: offer %xip for MAC address %02x-%02x\n", 1326 ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ), 1327 pxEndPoint->xMACAddress.ucBytes[ 4 ], 1328 pxEndPoint->xMACAddress.ucBytes[ 5 ] ) ); 1329 xReturn = pdPASS; 1330 } 1331 } 1332 } 1333 1334 if( pucUDPPayload != NULL ) 1335 { 1336 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayload ); 1337 } 1338 } /* if( lBytes > 0 ) */ 1339 1340 return xReturn; 1341 } 1342 /*-----------------------------------------------------------*/ 1343 1344 /** 1345 * @brief Create a partial DHCP message by filling in all the 'constant' fields. 1346 * 1347 * @param[out] pxAddress Address to be filled in. 1348 * @param[out] xOpcode Opcode to be filled in the packet. Will always be 'dhcpREQUEST_OPCODE'. 1349 * @param[in] pucOptionsArray The options to be added to the packet. 1350 * @param[in,out] pxOptionsArraySize Byte count of the options. Its value might change. 1351 * @param[in] pxEndPoint The end-point for which the request will be sent. 1352 * 1353 * @return Ethernet buffer of the partially created DHCP packet. 1354 */ prvCreatePartDHCPMessage(struct freertos_sockaddr * pxAddress,BaseType_t xOpcode,const uint8_t * const pucOptionsArray,size_t * pxOptionsArraySize,const NetworkEndPoint_t * pxEndPoint)1355 static uint8_t * prvCreatePartDHCPMessage( struct freertos_sockaddr * pxAddress, 1356 BaseType_t xOpcode, 1357 const uint8_t * const pucOptionsArray, 1358 size_t * pxOptionsArraySize, 1359 const NetworkEndPoint_t * pxEndPoint ) 1360 { 1361 DHCPMessage_IPv4_t * pxDHCPMessage; 1362 size_t uxRequiredBufferSize = sizeof( DHCPMessage_IPv4_t ) + *pxOptionsArraySize; 1363 const NetworkBufferDescriptor_t * pxNetworkBuffer; 1364 uint8_t * pucUDPPayloadBuffer = NULL; 1365 1366 #if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) 1367 const char * pucHostName = pcApplicationHostnameHook(); 1368 size_t uxNameLength = 0; 1369 1370 if( pucHostName != NULL ) 1371 { 1372 uxNameLength = strlen( pucHostName ); 1373 } 1374 1375 uint8_t * pucPtr; 1376 1377 /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ 1378 const void * pvCopySource; 1379 void * pvCopyDest; 1380 1381 /* Two extra bytes for option code and length. */ 1382 uxRequiredBufferSize += ( 2U + uxNameLength ); 1383 #endif /* if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) */ 1384 1385 /* Obtain a network buffer with the required amount of storage. It doesn't make much sense 1386 * to use a time-out here, because that would cause the IP-task to wait for itself. */ 1387 pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( UDPPacket_t ) + uxRequiredBufferSize, 0U ); 1388 1389 if( pxNetworkBuffer != NULL ) 1390 { 1391 /* Leave space for the UDP header. */ 1392 pucUDPPayloadBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); 1393 1394 /* MISRA Ref 11.3.1 [Misaligned access] */ 1395 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 1396 /* coverity[misra_c_2012_rule_11_3_violation] */ 1397 pxDHCPMessage = ( ( DHCPMessage_IPv4_t * ) pucUDPPayloadBuffer ); 1398 1399 { 1400 uint8_t * pucIPType; 1401 1402 /* Store the IP type at a known location. 1403 * Later the type must be known to translate 1404 * a payload- to a network buffer. 1405 */ 1406 pucIPType = pucUDPPayloadBuffer - ipUDP_PAYLOAD_IP_TYPE_OFFSET; 1407 *pucIPType = ipTYPE_IPv4; 1408 } 1409 1410 /* Most fields need to be zero. */ 1411 ( void ) memset( pxDHCPMessage, 0x00, sizeof( DHCPMessage_IPv4_t ) ); 1412 1413 /* Create the message. */ 1414 pxDHCPMessage->ucOpcode = ( uint8_t ) xOpcode; 1415 pxDHCPMessage->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET; 1416 pxDHCPMessage->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH; 1417 pxDHCPMessage->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId ); 1418 pxDHCPMessage->ulDHCPCookie = ( uint32_t ) dhcpCOOKIE; 1419 1420 if( EP_DHCPData.xUseBroadcast != pdFALSE ) 1421 { 1422 pxDHCPMessage->usFlags = ( uint16_t ) dhcpBROADCAST; 1423 } 1424 else 1425 { 1426 pxDHCPMessage->usFlags = 0U; 1427 } 1428 1429 ( void ) memcpy( &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), pxEndPoint->xMACAddress.ucBytes, sizeof( MACAddress_t ) ); 1430 1431 /* Copy in the const part of the options options. */ 1432 ( void ) memcpy( &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), pucOptionsArray, *pxOptionsArraySize ); 1433 1434 #if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) 1435 { 1436 /* With this option, the hostname can be registered as well which makes 1437 * it easier to lookup a device in a router's list of DHCP clients. */ 1438 1439 /* Point to where the OPTION_END was stored to add data. */ 1440 pucPtr = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + ( *pxOptionsArraySize - 1U ) ] ); 1441 pucPtr[ 0U ] = dhcpIPv4_DNS_HOSTNAME_OPTIONS_CODE; 1442 pucPtr[ 1U ] = ( uint8_t ) uxNameLength; 1443 1444 /* 1445 * Use helper variables for memcpy() to remain 1446 * compliant with MISRA Rule 21.15. These should be 1447 * optimized away. 1448 */ 1449 if( pucHostName != NULL ) 1450 { 1451 pvCopySource = pucHostName; 1452 pvCopyDest = &pucPtr[ 2U ]; 1453 1454 ( void ) memcpy( pvCopyDest, pvCopySource, uxNameLength ); 1455 } 1456 1457 pucPtr[ 2U + uxNameLength ] = ( uint8_t ) dhcpOPTION_END_BYTE; 1458 *pxOptionsArraySize += ( size_t ) ( 2U + uxNameLength ); 1459 } 1460 #endif /* if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) */ 1461 1462 /* Map in the client identifier. */ 1463 ( void ) memcpy( &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ), 1464 pxEndPoint->xMACAddress.ucBytes, sizeof( MACAddress_t ) ); 1465 1466 /* Set the addressing. */ 1467 pxAddress->sin_address.ulIP_IPv4 = ipBROADCAST_IP_ADDRESS; 1468 pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT_IPv4; 1469 pxAddress->sin_family = FREERTOS_AF_INET4; 1470 } 1471 1472 return pucUDPPayloadBuffer; 1473 } 1474 /*-----------------------------------------------------------*/ 1475 1476 /** 1477 * @brief Create and send a DHCP request message through the DHCP socket. 1478 * 1479 * @param[in] pxEndPoint The end-point for which the request will be sent. 1480 */ prvSendDHCPRequest(NetworkEndPoint_t * pxEndPoint)1481 static BaseType_t prvSendDHCPRequest( NetworkEndPoint_t * pxEndPoint ) 1482 { 1483 BaseType_t xResult = pdFAIL; 1484 uint8_t * pucUDPPayloadBuffer; 1485 struct freertos_sockaddr xAddress; 1486 static const uint8_t ucDHCPRequestOptions[] = 1487 { 1488 /* Do not change the ordering without also changing 1489 * dhcpCLIENT_IDENTIFIER_OFFSET, dhcpREQUESTED_IP_ADDRESS_OFFSET and 1490 * dhcpDHCP_SERVER_IP_ADDRESS_OFFSET. */ 1491 dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST, /* Message type option. */ 1492 dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 7, 1, 0, 0, 0, 0, 0, 0, /* Client identifier. */ 1493 dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */ 1494 dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address of the DHCP server. */ 1495 dhcpOPTION_END_BYTE 1496 }; 1497 size_t uxOptionsLength = sizeof( ucDHCPRequestOptions ); 1498 /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ 1499 const void * pvCopySource; 1500 void * pvCopyDest; 1501 1502 /* MISRA doesn't like uninitialised structs. */ 1503 ( void ) memset( &( xAddress ), 0, sizeof( xAddress ) ); 1504 pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, 1505 ( BaseType_t ) dhcpREQUEST_OPCODE, 1506 ucDHCPRequestOptions, 1507 &( uxOptionsLength ), 1508 pxEndPoint ); 1509 1510 /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ 1511 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ 1512 /* coverity[misra_c_2012_rule_11_4_violation] */ 1513 if( ( xSocketValid( xDHCPv4Socket ) == pdTRUE ) && ( pucUDPPayloadBuffer != NULL ) ) 1514 { 1515 /* Copy in the IP address being requested. */ 1516 1517 /* 1518 * Use helper variables for memcpy() source & dest to remain 1519 * compliant with MISRA Rule 21.15. These should be 1520 * optimized away. 1521 */ 1522 pvCopySource = &EP_DHCPData.ulOfferedIPAddress; 1523 pvCopyDest = &pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ]; 1524 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulOfferedIPAddress ) ); 1525 1526 /* Copy in the address of the DHCP server being used. */ 1527 pvCopySource = &EP_DHCPData.ulDHCPServerAddress; 1528 pvCopyDest = &pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ]; 1529 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulDHCPServerAddress ) ); 1530 1531 FreeRTOS_debug_printf( ( "vDHCPProcess: reply %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) ); 1532 iptraceSENDING_DHCP_REQUEST(); 1533 1534 xDHCPv4Socket->pxEndPoint = pxEndPoint; 1535 1536 if( FreeRTOS_sendto( xDHCPv4Socket, pucUDPPayloadBuffer, sizeof( DHCPMessage_IPv4_t ) + uxOptionsLength, FREERTOS_ZERO_COPY, &xAddress, ( socklen_t ) sizeof( xAddress ) ) == 0 ) 1537 { 1538 /* The packet was not successfully queued for sending and must be 1539 * returned to the stack. */ 1540 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); 1541 } 1542 else 1543 { 1544 xResult = pdPASS; 1545 } 1546 } 1547 1548 return xResult; 1549 } 1550 /*-----------------------------------------------------------*/ 1551 1552 /** 1553 * @brief Create and send a DHCP discover packet through the DHCP socket. 1554 * 1555 * @param[in] pxEndPoint the end-point for which the discover message will be sent. 1556 * 1557 * @return: pdPASS if the DHCP discover message was sent successfully, pdFAIL otherwise. 1558 */ prvSendDHCPDiscover(NetworkEndPoint_t * pxEndPoint)1559 static BaseType_t prvSendDHCPDiscover( NetworkEndPoint_t * pxEndPoint ) 1560 { 1561 BaseType_t xResult = pdFAIL; 1562 uint8_t * pucUDPPayloadBuffer; 1563 struct freertos_sockaddr xAddress; 1564 static const uint8_t ucDHCPDiscoverOptions[] = 1565 { 1566 /* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */ 1567 dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER, /* Message type option. */ 1568 dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 7, 1, 0, 0, 0, 0, 0, 0, /* Client identifier. */ 1569 dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */ 1570 dhcpIPv4_PARAMETER_REQUEST_OPTION_CODE, 3, dhcpIPv4_SUBNET_MASK_OPTION_CODE, dhcpIPv4_GATEWAY_OPTION_CODE, dhcpIPv4_DNS_SERVER_OPTIONS_CODE, /* Parameter request option. */ 1571 dhcpOPTION_END_BYTE 1572 }; 1573 size_t uxOptionsLength = sizeof( ucDHCPDiscoverOptions ); 1574 1575 ( void ) memset( &( xAddress ), 0, sizeof( xAddress ) ); 1576 pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, 1577 ( BaseType_t ) dhcpREQUEST_OPCODE, 1578 ucDHCPDiscoverOptions, 1579 &( uxOptionsLength ), 1580 pxEndPoint ); 1581 1582 /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ 1583 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ 1584 /* coverity[misra_c_2012_rule_11_4_violation] */ 1585 if( ( xSocketValid( xDHCPv4Socket ) == pdTRUE ) && ( pucUDPPayloadBuffer != NULL ) ) 1586 { 1587 const void * pvCopySource; 1588 void * pvCopyDest; 1589 1590 FreeRTOS_debug_printf( ( "vDHCPProcess: discover\n" ) ); 1591 iptraceSENDING_DHCP_DISCOVER(); 1592 1593 if( pxEndPoint->xDHCPData.ulPreferredIPAddress != 0U ) 1594 { 1595 /* Fill in the IPv4 address. */ 1596 pvCopySource = &( pxEndPoint->xDHCPData.ulPreferredIPAddress ); 1597 pvCopyDest = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ); 1598 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulPreferredIPAddress ) ); 1599 } 1600 else 1601 { 1602 /* Remove option-50 from the list because it is not used. */ 1603 size_t uxCopyLength; 1604 /* Exclude this line from branch coverage as the not-taken condition will never happen unless the code is modified */ 1605 configASSERT( uxOptionsLength > ( dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ) ); /* LCOV_EXCL_BR_LINE */ 1606 uxCopyLength = uxOptionsLength - ( dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ); 1607 pvCopySource = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ] ); 1608 pvCopyDest = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpOPTION_50_OFFSET ] ); 1609 ( void ) memmove( pvCopyDest, pvCopySource, uxCopyLength ); 1610 /* Send 6 bytes less than foreseen. */ 1611 uxOptionsLength -= dhcpOPTION_50_SIZE; 1612 } 1613 1614 xDHCPv4Socket->pxEndPoint = pxEndPoint; 1615 1616 if( FreeRTOS_sendto( xDHCPv4Socket, 1617 pucUDPPayloadBuffer, 1618 sizeof( DHCPMessage_IPv4_t ) + uxOptionsLength, 1619 FREERTOS_ZERO_COPY, 1620 &( xAddress ), 1621 ( socklen_t ) sizeof( xAddress ) ) == 0 ) 1622 { 1623 /* The packet was not successfully queued for sending and must be 1624 * returned to the stack. */ 1625 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); 1626 } 1627 else 1628 { 1629 xResult = pdTRUE; 1630 } 1631 } 1632 1633 return xResult; 1634 } 1635 /*-----------------------------------------------------------*/ 1636 1637 1638 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 1639 1640 /** 1641 * @brief When DHCP has failed, the code can assign a Link-Layer address, and check if 1642 * another device already uses the IP-address. 1643 * 1644 * param[in] pxEndPoint The end-point that wants to obtain a link-layer address. 1645 */ prvPrepareLinkLayerIPLookUp(NetworkEndPoint_t * pxEndPoint)1646 void prvPrepareLinkLayerIPLookUp( NetworkEndPoint_t * pxEndPoint ) 1647 { 1648 uint8_t ucLinkLayerIPAddress[ 2 ]; 1649 uint32_t ulNumbers[ 2 ]; 1650 1651 /* After DHCP has failed to answer, prepare everything to start 1652 * trying-out LinkLayer IP-addresses, using the random method. */ 1653 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 1654 1655 ( void ) xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); 1656 ( void ) xApplicationGetRandomNumber( &( ulNumbers[ 1 ] ) ); 1657 ucLinkLayerIPAddress[ 0 ] = ( uint8_t ) ( 1 + ( ulNumbers[ 0 ] % 0xFDU ) ); /* get value 1..254 for IP-address 3rd byte of IP address to try. */ 1658 ucLinkLayerIPAddress[ 1 ] = ( uint8_t ) ( 1 + ( ulNumbers[ 1 ] % 0xFDU ) ); /* get value 1..254 for IP-address 4th byte of IP address to try. */ 1659 1660 EP_IPv4_SETTINGS.ulGatewayAddress = 0U; 1661 1662 /* prepare xDHCPData with data to test. */ 1663 EP_DHCPData.ulOfferedIPAddress = 1664 FreeRTOS_inet_addr_quick( LINK_LAYER_ADDRESS_0, LINK_LAYER_ADDRESS_1, ucLinkLayerIPAddress[ 0 ], ucLinkLayerIPAddress[ 1 ] ); 1665 1666 EP_DHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; /* don't care about lease time. just put anything. */ 1667 1668 EP_IPv4_SETTINGS.ulNetMask = 1669 FreeRTOS_inet_addr_quick( LINK_LAYER_NETMASK_0, LINK_LAYER_NETMASK_1, LINK_LAYER_NETMASK_2, LINK_LAYER_NETMASK_3 ); 1670 1671 /* DHCP completed. The IP address can now be used, and the 1672 * timer set to the lease timeout time. */ 1673 EP_IPv4_SETTINGS.ulIPAddress = EP_DHCPData.ulOfferedIPAddress; 1674 1675 /* Setting the 'local' broadcast address, something like 192.168.1.255' */ 1676 EP_IPv4_SETTINGS.ulBroadcastAddress = ( EP_DHCPData.ulOfferedIPAddress & EP_IPv4_SETTINGS.ulNetMask ) | ~EP_IPv4_SETTINGS.ulNetMask; 1677 1678 /* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */ 1679 prvCloseDHCPSocket( pxEndPoint ); 1680 1681 xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); 1682 EP_DHCPData.xDHCPTxPeriod = pdMS_TO_TICKS( 3000U + ( ulNumbers[ 0 ] & 0x3ffU ) ); /* do ARP test every (3 + 0-1024mS) seconds. */ 1683 1684 xARPHadIPClash = pdFALSE; /* reset flag that shows if have ARP clash. */ 1685 vARPSendGratuitous(); 1686 } 1687 1688 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ 1689 /*-----------------------------------------------------------*/ 1690 1691 #endif /* ipconfigUSE_DHCP != 0 */ 1692