1 /* 2 * FreeRTOS+TCP V3.1.0 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 /** @brief The UDP socket used for all incoming and outgoing DHCP traffic. */ 58 _static Socket_t xDHCPSocket; 59 60 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 61 /* Define the Link Layer IP address: 169.254.x.x */ 62 #define LINK_LAYER_ADDRESS_0 169 63 #define LINK_LAYER_ADDRESS_1 254 64 65 /* Define the netmask used: 255.255.0.0 */ 66 #define LINK_LAYER_NETMASK_0 255 67 #define LINK_LAYER_NETMASK_1 255 68 #define LINK_LAYER_NETMASK_2 0 69 #define LINK_LAYER_NETMASK_3 0 70 #endif 71 72 73 /* 74 * Generate a DHCP discover message and send it on the DHCP socket. 75 */ 76 static BaseType_t prvSendDHCPDiscover( void ); 77 78 /* 79 * Interpret message received on the DHCP socket. 80 */ 81 _static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType ); 82 83 /* 84 * Generate a DHCP request packet, and send it on the DHCP socket. 85 */ 86 static BaseType_t prvSendDHCPRequest( void ); 87 88 /* 89 * Prepare to start a DHCP transaction. This initialises some state variables 90 * and creates the DHCP socket if necessary. 91 */ 92 static void prvInitialiseDHCP( void ); 93 94 /* 95 * Creates the part of outgoing DHCP messages that are common to all outgoing 96 * DHCP messages. 97 */ 98 static uint8_t * prvCreatePartDHCPMessage( struct freertos_sockaddr * pxAddress, 99 BaseType_t xOpcode, 100 const uint8_t * const pucOptionsArray, 101 size_t * pxOptionsArraySize ); 102 103 /* 104 * Create the DHCP socket, if it has not been created already. 105 */ 106 _static void prvCreateDHCPSocket( void ); 107 108 /* 109 * Close the DHCP socket. 110 */ 111 static void prvCloseDHCPSocket( void ); 112 113 /* 114 * After DHCP has failed to answer, prepare everything to start searching 115 * for (trying-out) LinkLayer IP-addresses, using the random method: Send 116 * a gratuitous ARP request and wait if another device responds to it. 117 */ 118 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 119 static void prvPrepareLinkLayerIPLookUp( void ); 120 #endif 121 122 /*-----------------------------------------------------------*/ 123 124 /** @brief Hold information in between steps in the DHCP state machine. */ 125 _static DHCPData_t xDHCPData; 126 127 /*-----------------------------------------------------------*/ 128 129 /** 130 * @brief Check whether a given socket is the DHCP socket or not. 131 * 132 * @param[in] xSocket: The socket to be checked. 133 * 134 * @return If the socket given as parameter is the DHCP socket - return 135 * pdTRUE, else pdFALSE. 136 */ xIsDHCPSocket(const ConstSocket_t xSocket)137 BaseType_t xIsDHCPSocket( const ConstSocket_t xSocket ) 138 { 139 BaseType_t xReturn; 140 141 if( xDHCPSocket == xSocket ) 142 { 143 xReturn = pdTRUE; 144 } 145 else 146 { 147 xReturn = pdFALSE; 148 } 149 150 return xReturn; 151 } 152 /*-----------------------------------------------------------*/ 153 154 /** 155 * @brief The application can indicate a preferred IP address by calling this function. 156 * before starting up the IP-task by calling FreeRTOS_IPInit(). 157 * 158 * @param[in] ulIPAddress: The preferred IP-address. 159 * 160 * @return The previous value of ulPreferredIPAddress. 161 */ vDHCPSetPreferredIPAddress(uint32_t ulIPAddress)162 uint32_t vDHCPSetPreferredIPAddress( uint32_t ulIPAddress ) 163 { 164 uint32_t ulPrevious = xDHCPData.ulPreferredIPAddress; 165 166 xDHCPData.ulPreferredIPAddress = ulIPAddress; 167 168 return ulPrevious; 169 } 170 171 /** 172 * @brief Returns the current state of a DHCP process. 173 * 174 * @return The current state ( eDHCPState_t ) of the DHCP process. 175 */ eGetDHCPState(void)176 eDHCPState_t eGetDHCPState( void ) 177 { 178 return EP_DHCPData.eDHCPState; 179 } 180 181 /** 182 * @brief Process the DHCP state machine based on current state. 183 * 184 * @param[in] xReset: Is the DHCP state machine starting over? pdTRUE/pdFALSE. 185 * @param[in] eExpectedState: The function will only run if the state is expected. 186 */ vDHCPProcess(BaseType_t xReset,eDHCPState_t eExpectedState)187 void vDHCPProcess( BaseType_t xReset, 188 eDHCPState_t eExpectedState ) 189 { 190 BaseType_t xGivingUp = pdFALSE; 191 192 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 193 eDHCPCallbackAnswer_t eAnswer; 194 #endif /* ipconfigUSE_DHCP_HOOK */ 195 196 /* Is DHCP starting over? */ 197 if( xReset != pdFALSE ) 198 { 199 EP_DHCPData.eDHCPState = eInitialWait; 200 } 201 202 if( ( EP_DHCPData.eDHCPState != eExpectedState ) && ( xReset == pdFALSE ) ) 203 { 204 /* When the DHCP event was generated, the DHCP client was 205 * in a different state. Therefore, ignore this event. */ 206 FreeRTOS_debug_printf( ( "DHCP wrong state: expect: %d got: %d : ignore\n", 207 eExpectedState, EP_DHCPData.eDHCPState ) ); 208 } 209 else 210 { 211 switch( EP_DHCPData.eDHCPState ) 212 { 213 case eInitialWait: 214 215 /* Initial state. Create the DHCP socket, timer, etc. if they 216 * have not already been created. */ 217 prvInitialiseDHCP(); 218 EP_DHCPData.eDHCPState = eWaitingSendFirstDiscover; 219 break; 220 221 case eWaitingSendFirstDiscover: 222 /* Ask the user if a DHCP discovery is required. */ 223 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 224 eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress ); 225 226 if( eAnswer == eDHCPContinue ) 227 #endif /* ipconfigUSE_DHCP_HOOK */ 228 { 229 /* See if prvInitialiseDHCP() has creates a socket. */ 230 if( xDHCPSocket == NULL ) 231 { 232 xGivingUp = pdTRUE; 233 } 234 else 235 { 236 *ipLOCAL_IP_ADDRESS_POINTER = 0U; 237 238 /* Send the first discover request. */ 239 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 240 241 if( prvSendDHCPDiscover() == pdPASS ) 242 { 243 EP_DHCPData.eDHCPState = eWaitingOffer; 244 } 245 else 246 { 247 /* Either the creation of a message buffer failed, or sendto(). 248 * Try again in the next cycle. */ 249 FreeRTOS_debug_printf( ( "Send failed during eWaitingSendFirstDiscover\n" ) ); 250 } 251 } 252 } 253 254 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 255 else 256 { 257 if( eAnswer == eDHCPUseDefaults ) 258 { 259 ( void ) memcpy( &( xNetworkAddressing ), &( xDefaultAddressing ), sizeof( xNetworkAddressing ) ); 260 } 261 262 /* The user indicates that the DHCP process does not continue. */ 263 xGivingUp = pdTRUE; 264 } 265 #endif /* ipconfigUSE_DHCP_HOOK */ 266 break; 267 268 case eSendDHCPRequest: 269 270 if( prvSendDHCPRequest() == pdPASS ) 271 { 272 /* Send succeeded, go to state 'eWaitingAcknowledge'. */ 273 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 274 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; 275 EP_DHCPData.eDHCPState = eWaitingAcknowledge; 276 } 277 else 278 { 279 /* Either the creation of a message buffer failed, or sendto(). 280 * Try again in the next cycle. */ 281 FreeRTOS_debug_printf( ( "Send failed during eSendDHCPRequest.\n" ) ); 282 } 283 284 break; 285 286 case eWaitingOffer: 287 288 xGivingUp = pdFALSE; 289 290 /* Look for offers coming in. */ 291 if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER ) == pdPASS ) 292 { 293 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 294 /* Ask the user if a DHCP request is required. */ 295 eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, EP_DHCPData.ulOfferedIPAddress ); 296 297 if( eAnswer == eDHCPContinue ) 298 #endif /* ipconfigUSE_DHCP_HOOK */ 299 { 300 /* An offer has been made, the user wants to continue, 301 * generate the request. */ 302 if( prvSendDHCPRequest() == pdPASS ) 303 { 304 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 305 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; 306 EP_DHCPData.eDHCPState = eWaitingAcknowledge; 307 } 308 else 309 { 310 /* Either the creation of a message buffer failed, or sendto(). 311 * Try again in the next cycle. */ 312 FreeRTOS_debug_printf( ( "Send failed during eWaitingOffer/1.\n" ) ); 313 EP_DHCPData.eDHCPState = eSendDHCPRequest; 314 } 315 316 break; 317 } 318 319 #if ( ipconfigUSE_DHCP_HOOK != 0 ) 320 if( eAnswer == eDHCPUseDefaults ) 321 { 322 ( void ) memcpy( &( xNetworkAddressing ), &( xDefaultAddressing ), sizeof( xNetworkAddressing ) ); 323 } 324 325 /* The user indicates that the DHCP process does not continue. */ 326 xGivingUp = pdTRUE; 327 #endif /* ipconfigUSE_DHCP_HOOK */ 328 } 329 330 /* Is it time to send another Discover? */ 331 else if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) 332 { 333 /* It is time to send another Discover. Increase the time 334 * period, and if it has not got to the point of giving up - send 335 * another discovery. */ 336 EP_DHCPData.xDHCPTxPeriod <<= 1; 337 338 if( EP_DHCPData.xDHCPTxPeriod <= ( TickType_t ) ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) 339 { 340 if( xApplicationGetRandomNumber( &( EP_DHCPData.ulTransactionId ) ) != pdFALSE ) 341 { 342 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 343 344 if( EP_DHCPData.xUseBroadcast != pdFALSE ) 345 { 346 EP_DHCPData.xUseBroadcast = pdFALSE; 347 } 348 else 349 { 350 EP_DHCPData.xUseBroadcast = pdTRUE; 351 } 352 353 if( prvSendDHCPDiscover() == pdPASS ) 354 { 355 FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", EP_DHCPData.xDHCPTxPeriod ) ); 356 } 357 else 358 { 359 /* Either the creation of a message buffer failed, or sendto(). 360 * Try again in the next cycle. */ 361 FreeRTOS_debug_printf( ( "Send failed during eWaitingOffer/2.\n" ) ); 362 EP_DHCPData.eDHCPState = eInitialWait; 363 } 364 } 365 else 366 { 367 FreeRTOS_debug_printf( ( "vDHCPProcess: failed to generate a random Transaction ID\n" ) ); 368 } 369 } 370 else 371 { 372 FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", EP_DHCPData.xDHCPTxPeriod, ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) ); 373 374 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 375 { 376 /* Only use a fake Ack if the default IP address == 0x00 377 * and the link local addressing is used. Start searching 378 * a free LinkLayer IP-address. Next state will be 379 * 'eGetLinkLayerAddress'. */ 380 prvPrepareLinkLayerIPLookUp(); 381 382 /* Setting an IP address manually so set to not using 383 * leased address mode. */ 384 EP_DHCPData.eDHCPState = eGetLinkLayerAddress; 385 } 386 #else 387 { 388 xGivingUp = pdTRUE; 389 } 390 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ 391 } 392 } 393 else 394 { 395 /* There was no DHCP reply, there was no time-out, just keep on waiting. */ 396 } 397 398 break; 399 400 case eWaitingAcknowledge: 401 402 /* Look for acks coming in. */ 403 if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK ) == pdPASS ) 404 { 405 FreeRTOS_debug_printf( ( "vDHCPProcess: acked %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) ); 406 407 /* DHCP completed. The IP address can now be used, and the 408 * timer set to the lease timeout time. */ 409 *ipLOCAL_IP_ADDRESS_POINTER = EP_DHCPData.ulOfferedIPAddress; 410 411 /* Setting the 'local' broadcast address, something like 412 * '192.168.1.255'. */ 413 EP_IPv4_SETTINGS.ulBroadcastAddress = ( EP_DHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask; 414 EP_DHCPData.eDHCPState = eLeasedAddress; 415 416 iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress ); 417 418 /* DHCP failed, the default configured IP-address will be used 419 * Now call vIPNetworkUpCalls() to send the network-up event and 420 * start the ARP timer. */ 421 vIPNetworkUpCalls(); 422 423 /* Close socket to ensure packets don't queue on it. */ 424 prvCloseDHCPSocket(); 425 426 if( EP_DHCPData.ulLeaseTime == 0U ) 427 { 428 EP_DHCPData.ulLeaseTime = ( uint32_t ) dhcpDEFAULT_LEASE_TIME; 429 } 430 else if( EP_DHCPData.ulLeaseTime < dhcpMINIMUM_LEASE_TIME ) 431 { 432 EP_DHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME; 433 } 434 else 435 { 436 /* The lease time is already valid. */ 437 } 438 439 /* Check for clashes. */ 440 vARPSendGratuitous(); 441 vDHCPTimerReload( EP_DHCPData.ulLeaseTime ); 442 } 443 else 444 { 445 /* Is it time to send another Discover? */ 446 if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) 447 { 448 /* Increase the time period, and if it has not got to the 449 * point of giving up - send another request. */ 450 EP_DHCPData.xDHCPTxPeriod <<= 1; 451 452 if( EP_DHCPData.xDHCPTxPeriod <= ( TickType_t ) ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) 453 { 454 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 455 456 if( prvSendDHCPRequest() == pdPASS ) 457 { 458 /* The message is sent. Stay in state 'eWaitingAcknowledge'. */ 459 } 460 else 461 { 462 /* Either the creation of a message buffer failed, or sendto(). 463 * Try again in the next cycle. */ 464 FreeRTOS_debug_printf( ( "Send failed during eWaitingAcknowledge.\n" ) ); 465 EP_DHCPData.eDHCPState = eSendDHCPRequest; 466 } 467 } 468 else 469 { 470 /* Give up, start again. */ 471 EP_DHCPData.eDHCPState = eInitialWait; 472 } 473 } 474 } 475 476 break; 477 478 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 479 case eGetLinkLayerAddress: 480 481 if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) 482 { 483 if( xARPHadIPClash == pdFALSE ) 484 { 485 /* ARP OK. proceed. */ 486 iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress ); 487 488 /* Auto-IP succeeded, the default configured IP-address will 489 * be used. Now call vIPNetworkUpCalls() to send the 490 * network-up event and start the ARP timer. */ 491 vIPNetworkUpCalls(); 492 EP_DHCPData.eDHCPState = eNotUsingLeasedAddress; 493 } 494 else 495 { 496 /* ARP clashed - try another IP address. */ 497 prvPrepareLinkLayerIPLookUp(); 498 499 /* Setting an IP address manually so set to not using leased 500 * address mode. */ 501 EP_DHCPData.eDHCPState = eGetLinkLayerAddress; 502 } 503 } 504 break; 505 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ 506 507 case eLeasedAddress: 508 509 if( FreeRTOS_IsNetworkUp() != 0 ) 510 { 511 /* Resend the request at the appropriate time to renew the lease. */ 512 prvCreateDHCPSocket(); 513 514 if( xDHCPSocket != NULL ) 515 { 516 uint32_t ulID = 0U; 517 518 if( xApplicationGetRandomNumber( &( ulID ) ) != pdFALSE ) 519 { 520 EP_DHCPData.ulTransactionId = ulID; 521 } 522 523 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 524 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; 525 526 if( prvSendDHCPRequest() == pdPASS ) 527 { 528 /* The packet was sent successfully, wait for an acknowledgement. */ 529 EP_DHCPData.eDHCPState = eWaitingAcknowledge; 530 } 531 else 532 { 533 /* The packet was not sent, try sending it later. */ 534 EP_DHCPData.eDHCPState = eSendDHCPRequest; 535 FreeRTOS_debug_printf( ( "Send failed eLeasedAddress.\n" ) ); 536 } 537 538 /* From now on, we should be called more often */ 539 vDHCPTimerReload( dhcpINITIAL_TIMER_PERIOD ); 540 } 541 } 542 else 543 { 544 /* See PR #53 on github/freertos/freertos */ 545 FreeRTOS_printf( ( "DHCP: lease time finished but network is down\n" ) ); 546 vDHCPTimerReload( pdMS_TO_TICKS( 5000U ) ); 547 } 548 549 break; 550 551 case eNotUsingLeasedAddress: 552 553 vIPSetDHCPTimerEnableState( pdFALSE ); 554 break; 555 556 default: 557 /* Lint: all options are included. */ 558 break; 559 } 560 561 if( xGivingUp != pdFALSE ) 562 { 563 /* xGivingUp became true either because of a time-out, or because 564 * xApplicationDHCPHook() returned another value than 'eDHCPContinue', 565 * meaning that the conversion is cancelled from here. */ 566 567 /* Revert to static IP address. */ 568 taskENTER_CRITICAL(); 569 { 570 *ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress; 571 iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( xNetworkAddressing.ulDefaultIPAddress ); 572 } 573 taskEXIT_CRITICAL(); 574 575 EP_DHCPData.eDHCPState = eNotUsingLeasedAddress; 576 vIPSetDHCPTimerEnableState( pdFALSE ); 577 578 /* DHCP failed, the default configured IP-address will be used. Now 579 * call vIPNetworkUpCalls() to send the network-up event and start the ARP 580 * timer. */ 581 vIPNetworkUpCalls(); 582 583 /* Close socket to ensure packets don't queue on it. */ 584 prvCloseDHCPSocket(); 585 } 586 } 587 } 588 /*-----------------------------------------------------------*/ 589 590 /** 591 * @brief Close the DHCP socket. 592 */ prvCloseDHCPSocket(void)593 static void prvCloseDHCPSocket( void ) 594 { 595 if( xDHCPSocket != NULL ) 596 { 597 /* This modules runs from the IP-task. Use the internal 598 * function 'vSocketClose()` to close the socket. */ 599 ( void ) vSocketClose( xDHCPSocket ); 600 xDHCPSocket = NULL; 601 } 602 } 603 /*-----------------------------------------------------------*/ 604 605 /** 606 * @brief Create a DHCP socket with the defined timeouts. 607 */ prvCreateDHCPSocket(void)608 _static void prvCreateDHCPSocket( void ) 609 { 610 struct freertos_sockaddr xAddress; 611 BaseType_t xReturn; 612 TickType_t xTimeoutTime = ( TickType_t ) 0; 613 614 /* Create the socket, if it has not already been created. */ 615 if( xDHCPSocket == NULL ) 616 { 617 xDHCPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); 618 619 /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ 620 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ 621 /* coverity[misra_c_2012_rule_11_4_violation] */ 622 if( xDHCPSocket != FREERTOS_INVALID_SOCKET ) 623 { 624 /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the 625 * context of the IP task. */ 626 ( void ) FreeRTOS_setsockopt( xDHCPSocket, 0, FREERTOS_SO_RCVTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) ); 627 ( void ) FreeRTOS_setsockopt( xDHCPSocket, 0, FREERTOS_SO_SNDTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) ); 628 629 /* Bind to the standard DHCP client port. */ 630 xAddress.sin_port = ( uint16_t ) dhcpCLIENT_PORT_IPv4; 631 xReturn = vSocketBind( xDHCPSocket, &xAddress, sizeof( xAddress ), pdFALSE ); 632 633 if( xReturn != 0 ) 634 { 635 /* Binding failed, close the socket again. */ 636 prvCloseDHCPSocket(); 637 } 638 } 639 else 640 { 641 /* Change to NULL for easier testing. */ 642 xDHCPSocket = NULL; 643 } 644 } 645 } 646 /*-----------------------------------------------------------*/ 647 648 /** 649 * @brief Initialise the DHCP state machine by creating DHCP socket and 650 * begin the transaction. 651 */ prvInitialiseDHCP(void)652 static void prvInitialiseDHCP( void ) 653 { 654 /* Initialise the parameters that will be set by the DHCP process. Per 655 * https://www.ietf.org/rfc/rfc2131.txt, Transaction ID should be a random 656 * value chosen by the client. */ 657 658 /* Check for random number generator API failure. */ 659 if( xApplicationGetRandomNumber( &( EP_DHCPData.ulTransactionId ) ) != pdFALSE ) 660 { 661 EP_DHCPData.xUseBroadcast = 0; 662 EP_DHCPData.ulOfferedIPAddress = 0U; 663 EP_DHCPData.ulDHCPServerAddress = 0U; 664 EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; 665 666 /* Create the DHCP socket if it has not already been created. */ 667 prvCreateDHCPSocket(); 668 FreeRTOS_debug_printf( ( "prvInitialiseDHCP: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) ); 669 vDHCPTimerReload( dhcpINITIAL_TIMER_PERIOD ); 670 } 671 else 672 { 673 /* There was a problem with the randomiser. */ 674 } 675 } 676 /*-----------------------------------------------------------*/ 677 678 /** 679 * @brief Check whether the DHCP response from the server has all valid 680 * invariant parameters and valid (non broadcast and non localhost) 681 * IP address being assigned to the device. 682 * 683 * @param[in] pxDHCPMessage: The DHCP message. 684 * 685 * @return pdPASS if the DHCP response has correct parameters; pdFAIL otherwise. 686 */ prvIsValidDHCPResponse(const DHCPMessage_IPv4_t * pxDHCPMessage)687 static BaseType_t prvIsValidDHCPResponse( const DHCPMessage_IPv4_t * pxDHCPMessage ) 688 { 689 BaseType_t xReturn = pdPASS; 690 691 if( ( pxDHCPMessage->ulDHCPCookie != ( uint32_t ) dhcpCOOKIE ) || 692 ( pxDHCPMessage->ucOpcode != ( uint8_t ) dhcpREPLY_OPCODE ) || 693 ( pxDHCPMessage->ucAddressType != ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET ) || 694 ( pxDHCPMessage->ucAddressLength != ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH ) || 695 ( ( FreeRTOS_ntohl( pxDHCPMessage->ulYourIPAddress_yiaddr ) & 0xFFU ) == 0xFFU ) || 696 ( ( ( pxDHCPMessage->ulYourIPAddress_yiaddr & 0x7FU ) ^ 0x7FU ) == 0x00U ) ) 697 { 698 /* Invalid cookie OR 699 * Unexpected opcode OR 700 * Incorrect address type OR 701 * Incorrect address length OR 702 * The DHCP server is trying to assign a broadcast address to the device OR 703 * The DHCP server is trying to assign a localhost address to the device. */ 704 xReturn = pdFAIL; 705 } 706 707 return xReturn; 708 } 709 710 /** 711 * @brief Process the DHCP replies. 712 * 713 * @param[in] xExpectedMessageType: The type of the message the DHCP state machine is expecting. 714 * Messages of different type will be dropped. 715 * 716 * @return pdPASS: if DHCP options are received correctly; pdFAIL: Otherwise. 717 */ prvProcessDHCPReplies(BaseType_t xExpectedMessageType)718 _static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType ) 719 { 720 uint8_t * pucUDPPayload; 721 int32_t lBytes; 722 const DHCPMessage_IPv4_t * pxDHCPMessage; 723 const uint8_t * pucByte; 724 uint8_t ucOptionCode; 725 uint32_t ulProcessed, ulParameter; 726 BaseType_t xReturn = pdFALSE; 727 const uint32_t ulMandatoryOptions = 2U; /* DHCP server address, and the correct DHCP message type must be present in the options. */ 728 /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ 729 const void * pvCopySource; 730 void * pvCopyDest; 731 732 /* Passing the address of a pointer (pucUDPPayload) because FREERTOS_ZERO_COPY is used. */ 733 lBytes = FreeRTOS_recvfrom( xDHCPSocket, &pucUDPPayload, 0U, FREERTOS_ZERO_COPY, NULL, NULL ); 734 735 if( lBytes > 0 ) 736 { 737 /* Map a DHCP structure onto the received data. */ 738 739 /* MISRA Ref 11.3.1 [Misaligned access] */ 740 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 741 /* coverity[misra_c_2012_rule_11_3_violation] */ 742 pxDHCPMessage = ( ( DHCPMessage_IPv4_t * ) pucUDPPayload ); 743 744 /* Sanity check. */ 745 if( lBytes < ( int32_t ) sizeof( DHCPMessage_IPv4_t ) ) 746 { 747 /* Not enough bytes. */ 748 } 749 else if( prvIsValidDHCPResponse( pxDHCPMessage ) == pdFAIL ) 750 { 751 /* Invalid values in DHCP response. */ 752 } 753 else if( ( pxDHCPMessage->ulTransactionID != FreeRTOS_htonl( EP_DHCPData.ulTransactionId ) ) ) 754 { 755 /* Transaction ID does not match. */ 756 } 757 else /* Looks like a valid DHCP response, with the same transaction ID. */ 758 { 759 if( memcmp( pxDHCPMessage->ucClientHardwareAddress, 760 ipLOCAL_MAC_ADDRESS, 761 sizeof( MACAddress_t ) ) != 0 ) 762 { 763 /* Target MAC address doesn't match. */ 764 } 765 else 766 { 767 size_t uxIndex, uxPayloadDataLength, uxLength; 768 769 /* None of the essential options have been processed yet. */ 770 ulProcessed = 0U; 771 772 /* Walk through the options until the dhcpOPTION_END_BYTE byte 773 * is found, taking care not to walk off the end of the options. */ 774 pucByte = &( pucUDPPayload[ sizeof( DHCPMessage_IPv4_t ) ] ); 775 uxIndex = 0; 776 uxPayloadDataLength = ( ( size_t ) lBytes ) - sizeof( DHCPMessage_IPv4_t ); 777 778 while( uxIndex < uxPayloadDataLength ) 779 { 780 ucOptionCode = pucByte[ uxIndex ]; 781 782 if( ucOptionCode == ( uint8_t ) dhcpOPTION_END_BYTE ) 783 { 784 /* Ready, the last byte has been seen. */ 785 /* coverity[break_stmt] : Break statement terminating the loop */ 786 break; 787 } 788 789 if( ucOptionCode == ( uint8_t ) dhcpIPv4_ZERO_PAD_OPTION_CODE ) 790 { 791 /* The value zero is used as a pad byte, 792 * it is not followed by a length byte. */ 793 uxIndex = uxIndex + 1U; 794 continue; 795 } 796 797 /* Stop if the response is malformed. */ 798 if( ( uxIndex + 1U ) < uxPayloadDataLength ) 799 { 800 /* Fetch the length byte. */ 801 uxLength = ( size_t ) pucByte[ uxIndex + 1U ]; 802 uxIndex = uxIndex + 2U; 803 804 if( !( ( ( uxIndex + uxLength ) - 1U ) < uxPayloadDataLength ) ) 805 { 806 /* There are not as many bytes left as there should be. */ 807 break; 808 } 809 } 810 else 811 { 812 /* The length byte is missing. */ 813 break; 814 } 815 816 /* In most cases, a 4-byte network-endian parameter follows, 817 * just get it once here and use later. */ 818 if( uxLength >= sizeof( ulParameter ) ) 819 { 820 /* 821 * Use helper variables for memcpy() to remain 822 * compliant with MISRA Rule 21.15. These should be 823 * optimized away. 824 */ 825 pvCopySource = &pucByte[ uxIndex ]; 826 pvCopyDest = &ulParameter; 827 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( ulParameter ) ); 828 /* 'uxIndex' will be increased at the end of this loop. */ 829 } 830 else 831 { 832 ulParameter = 0; 833 } 834 835 /* Confirm uxIndex is still a valid index after adjustments to uxIndex above */ 836 if( !( uxIndex < uxPayloadDataLength ) ) 837 { 838 break; 839 } 840 841 /* Option-specific handling. */ 842 switch( ucOptionCode ) 843 { 844 case dhcpIPv4_MESSAGE_TYPE_OPTION_CODE: 845 846 if( pucByte[ uxIndex ] == ( uint8_t ) xExpectedMessageType ) 847 { 848 /* The message type is the message type the 849 * state machine is expecting. */ 850 ulProcessed++; 851 } 852 else 853 { 854 if( pucByte[ uxIndex ] == ( uint8_t ) dhcpMESSAGE_TYPE_NACK ) 855 { 856 if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_ACK ) 857 { 858 /* Start again. */ 859 EP_DHCPData.eDHCPState = eInitialWait; 860 } 861 } 862 863 /* Stop processing further options. */ 864 uxLength = 0; 865 } 866 867 break; 868 869 case dhcpIPv4_SUBNET_MASK_OPTION_CODE: 870 871 if( uxLength == sizeof( uint32_t ) ) 872 { 873 EP_IPv4_SETTINGS.ulNetMask = ulParameter; 874 } 875 876 break; 877 878 case dhcpIPv4_GATEWAY_OPTION_CODE: 879 880 /* The DHCP server may send more than 1 gateway addresses. */ 881 if( uxLength >= sizeof( uint32_t ) ) 882 { 883 /* ulProcessed is not incremented in this case 884 * because the gateway is not essential. */ 885 EP_IPv4_SETTINGS.ulGatewayAddress = ulParameter; 886 } 887 888 break; 889 890 case dhcpIPv4_DNS_SERVER_OPTIONS_CODE: 891 892 /* The DHCP server may send more than 1 DNS server addresses. */ 893 if( uxLength >= sizeof( uint32_t ) ) 894 { 895 /* ulProcessed is not incremented in this case 896 * because the DNS server is not essential. Only the 897 * first DNS server address is taken. */ 898 EP_IPv4_SETTINGS.ulDNSServerAddress = ulParameter; 899 } 900 901 break; 902 903 case dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE: 904 905 if( uxLength == sizeof( uint32_t ) ) 906 { 907 if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_OFFER ) 908 { 909 /* Offers state the replying server. */ 910 ulProcessed++; 911 EP_DHCPData.ulDHCPServerAddress = ulParameter; 912 } 913 else 914 { 915 /* The ack must come from the expected server. */ 916 if( EP_DHCPData.ulDHCPServerAddress == ulParameter ) 917 { 918 ulProcessed++; 919 } 920 } 921 } 922 923 break; 924 925 case dhcpIPv4_LEASE_TIME_OPTION_CODE: 926 927 if( uxLength == sizeof( EP_DHCPData.ulLeaseTime ) ) 928 { 929 /* ulProcessed is not incremented in this case 930 * because the lease time is not essential. */ 931 932 /* The DHCP parameter is in seconds, convert 933 * to host-endian format. */ 934 EP_DHCPData.ulLeaseTime = FreeRTOS_ntohl( ulParameter ); 935 936 /* Divide the lease time by two to ensure a renew 937 * request is sent before the lease actually expires. */ 938 EP_DHCPData.ulLeaseTime >>= 1; 939 940 /* Multiply with configTICK_RATE_HZ to get clock ticks. */ 941 EP_DHCPData.ulLeaseTime = ( uint32_t ) configTICK_RATE_HZ * ( uint32_t ) EP_DHCPData.ulLeaseTime; 942 } 943 944 break; 945 946 default: 947 948 /* Not interested in this field. */ 949 950 break; 951 } 952 953 /* Jump over the data to find the next option code. */ 954 if( uxLength == 0U ) 955 { 956 break; 957 } 958 959 uxIndex = uxIndex + uxLength; 960 } 961 962 /* Were all the mandatory options received? */ 963 if( ulProcessed >= ulMandatoryOptions ) 964 { 965 /* HT:endian: used to be network order */ 966 EP_DHCPData.ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr; 967 FreeRTOS_printf( ( "vDHCPProcess: offer %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) ); 968 xReturn = pdPASS; 969 } 970 } 971 } 972 973 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayload ); 974 } /* if( lBytes > 0 ) */ 975 976 return xReturn; 977 } 978 /*-----------------------------------------------------------*/ 979 980 /** 981 * @brief Create a partial DHCP message by filling in all the 'constant' fields. 982 * 983 * @param[out] pxAddress: Address to be filled in. 984 * @param[out] xOpcode: Opcode to be filled in the packet. Will always be 'dhcpREQUEST_OPCODE'. 985 * @param[in] pucOptionsArray: The options to be added to the packet. 986 * @param[in,out] pxOptionsArraySize: Byte count of the options. Its value might change. 987 * 988 * @return Ethernet buffer of the partially created DHCP packet. 989 */ prvCreatePartDHCPMessage(struct freertos_sockaddr * pxAddress,BaseType_t xOpcode,const uint8_t * const pucOptionsArray,size_t * pxOptionsArraySize)990 static uint8_t * prvCreatePartDHCPMessage( struct freertos_sockaddr * pxAddress, 991 BaseType_t xOpcode, 992 const uint8_t * const pucOptionsArray, 993 size_t * pxOptionsArraySize ) 994 { 995 DHCPMessage_IPv4_t * pxDHCPMessage; 996 size_t uxRequiredBufferSize = sizeof( DHCPMessage_IPv4_t ) + *pxOptionsArraySize; 997 const NetworkBufferDescriptor_t * pxNetworkBuffer; 998 uint8_t * pucUDPPayloadBuffer = NULL; 999 1000 #if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) 1001 const char * pucHostName = pcApplicationHostnameHook(); 1002 size_t uxNameLength = strlen( pucHostName ); 1003 uint8_t * pucPtr; 1004 1005 /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ 1006 const void * pvCopySource; 1007 void * pvCopyDest; 1008 1009 /* Two extra bytes for option code and length. */ 1010 uxRequiredBufferSize += ( 2U + uxNameLength ); 1011 #endif /* if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) */ 1012 1013 /* Obtain a network buffer with the required amount of storage. It doesn't make much sense 1014 * to use a time-out here, because that would cause the IP-task to wait for itself. */ 1015 pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( UDPPacket_t ) + uxRequiredBufferSize, 0U ); 1016 1017 if( pxNetworkBuffer != NULL ) 1018 { 1019 /* Leave space for the UDP header. */ 1020 pucUDPPayloadBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); 1021 1022 /* MISRA Ref 11.3.1 [Misaligned access] */ 1023 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 1024 /* coverity[misra_c_2012_rule_11_3_violation] */ 1025 pxDHCPMessage = ( ( DHCPMessage_IPv4_t * ) pucUDPPayloadBuffer ); 1026 1027 /* Most fields need to be zero. */ 1028 ( void ) memset( pxDHCPMessage, 0x00, sizeof( DHCPMessage_IPv4_t ) ); 1029 1030 /* Create the message. */ 1031 pxDHCPMessage->ucOpcode = ( uint8_t ) xOpcode; 1032 pxDHCPMessage->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET; 1033 pxDHCPMessage->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH; 1034 pxDHCPMessage->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId ); 1035 pxDHCPMessage->ulDHCPCookie = ( uint32_t ) dhcpCOOKIE; 1036 1037 if( EP_DHCPData.xUseBroadcast != pdFALSE ) 1038 { 1039 pxDHCPMessage->usFlags = ( uint16_t ) dhcpBROADCAST; 1040 } 1041 else 1042 { 1043 pxDHCPMessage->usFlags = 0U; 1044 } 1045 1046 ( void ) memcpy( &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ); 1047 1048 /* Copy in the const part of the options options. */ 1049 ( void ) memcpy( &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), pucOptionsArray, *pxOptionsArraySize ); 1050 1051 #if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) 1052 { 1053 /* With this option, the hostname can be registered as well which makes 1054 * it easier to lookup a device in a router's list of DHCP clients. */ 1055 1056 /* Point to where the OPTION_END was stored to add data. */ 1057 pucPtr = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + ( *pxOptionsArraySize - 1U ) ] ); 1058 pucPtr[ 0U ] = dhcpIPv4_DNS_HOSTNAME_OPTIONS_CODE; 1059 pucPtr[ 1U ] = ( uint8_t ) uxNameLength; 1060 1061 /* 1062 * Use helper variables for memcpy() to remain 1063 * compliant with MISRA Rule 21.15. These should be 1064 * optimized away. 1065 */ 1066 pvCopySource = pucHostName; 1067 pvCopyDest = &pucPtr[ 2U ]; 1068 1069 ( void ) memcpy( pvCopyDest, pvCopySource, uxNameLength ); 1070 pucPtr[ 2U + uxNameLength ] = ( uint8_t ) dhcpOPTION_END_BYTE; 1071 *pxOptionsArraySize += ( size_t ) ( 2U + uxNameLength ); 1072 } 1073 #endif /* if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) */ 1074 1075 /* Map in the client identifier. */ 1076 ( void ) memcpy( &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ), 1077 ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ); 1078 1079 /* Set the addressing. */ 1080 pxAddress->sin_addr = ipBROADCAST_IP_ADDRESS; 1081 pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT_IPv4; 1082 } 1083 1084 return pucUDPPayloadBuffer; 1085 } 1086 /*-----------------------------------------------------------*/ 1087 1088 /** 1089 * @brief Create and send a DHCP request message through the DHCP socket. 1090 * @return Returns pdPASS when the message is successfully created and sent. 1091 */ prvSendDHCPRequest(void)1092 static BaseType_t prvSendDHCPRequest( void ) 1093 { 1094 BaseType_t xResult = pdFAIL; 1095 uint8_t * pucUDPPayloadBuffer; 1096 struct freertos_sockaddr xAddress; 1097 static const uint8_t ucDHCPRequestOptions[] = 1098 { 1099 /* Do not change the ordering without also changing 1100 * dhcpCLIENT_IDENTIFIER_OFFSET, dhcpREQUESTED_IP_ADDRESS_OFFSET and 1101 * dhcpDHCP_SERVER_IP_ADDRESS_OFFSET. */ 1102 dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST, /* Message type option. */ 1103 dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 7, 1, 0, 0, 0, 0, 0, 0, /* Client identifier. */ 1104 dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */ 1105 dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address of the DHCP server. */ 1106 dhcpOPTION_END_BYTE 1107 }; 1108 size_t uxOptionsLength = sizeof( ucDHCPRequestOptions ); 1109 /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ 1110 const void * pvCopySource; 1111 void * pvCopyDest; 1112 1113 pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, 1114 ( BaseType_t ) dhcpREQUEST_OPCODE, 1115 ucDHCPRequestOptions, 1116 &( uxOptionsLength ) ); 1117 1118 if( pucUDPPayloadBuffer != NULL ) 1119 { 1120 /* Copy in the IP address being requested. */ 1121 1122 /* 1123 * Use helper variables for memcpy() source & dest to remain 1124 * compliant with MISRA Rule 21.15. These should be 1125 * optimized away. 1126 */ 1127 pvCopySource = &EP_DHCPData.ulOfferedIPAddress; 1128 pvCopyDest = &pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ]; 1129 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulOfferedIPAddress ) ); 1130 1131 /* Copy in the address of the DHCP server being used. */ 1132 pvCopySource = &EP_DHCPData.ulDHCPServerAddress; 1133 pvCopyDest = &pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ]; 1134 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulDHCPServerAddress ) ); 1135 1136 FreeRTOS_debug_printf( ( "vDHCPProcess: reply %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) ); 1137 iptraceSENDING_DHCP_REQUEST(); 1138 1139 if( FreeRTOS_sendto( xDHCPSocket, pucUDPPayloadBuffer, sizeof( DHCPMessage_IPv4_t ) + uxOptionsLength, FREERTOS_ZERO_COPY, &xAddress, ( socklen_t ) sizeof( xAddress ) ) == 0 ) 1140 { 1141 /* The packet was not successfully queued for sending and must be 1142 * returned to the stack. */ 1143 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); 1144 } 1145 else 1146 { 1147 xResult = pdPASS; 1148 } 1149 } 1150 1151 return xResult; 1152 } 1153 /*-----------------------------------------------------------*/ 1154 1155 /** 1156 * @brief Create and send a DHCP discover packet through the DHCP socket. 1157 * @return Returns pdPASS when the message is successfully created and sent. 1158 */ prvSendDHCPDiscover(void)1159 static BaseType_t prvSendDHCPDiscover( void ) 1160 { 1161 BaseType_t xResult = pdFAIL; 1162 uint8_t * pucUDPPayloadBuffer; 1163 struct freertos_sockaddr xAddress; 1164 static const uint8_t ucDHCPDiscoverOptions[] = 1165 { 1166 /* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */ 1167 dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER, /* Message type option. */ 1168 dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 7, 1, 0, 0, 0, 0, 0, 0, /* Client identifier. */ 1169 dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */ 1170 dhcpIPv4_PARAMETER_REQUEST_OPTION_CODE, 3, dhcpIPv4_SUBNET_MASK_OPTION_CODE, dhcpIPv4_GATEWAY_OPTION_CODE, dhcpIPv4_DNS_SERVER_OPTIONS_CODE, /* Parameter request option. */ 1171 dhcpOPTION_END_BYTE 1172 }; 1173 size_t uxOptionsLength = sizeof( ucDHCPDiscoverOptions ); 1174 1175 pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, 1176 ( BaseType_t ) dhcpREQUEST_OPCODE, 1177 ucDHCPDiscoverOptions, 1178 &( uxOptionsLength ) ); 1179 1180 if( pucUDPPayloadBuffer != NULL ) 1181 { 1182 const void * pvCopySource; 1183 void * pvCopyDest; 1184 1185 FreeRTOS_debug_printf( ( "vDHCPProcess: discover\n" ) ); 1186 iptraceSENDING_DHCP_DISCOVER(); 1187 1188 if( xDHCPData.ulPreferredIPAddress != 0U ) 1189 { 1190 /* Fill in the IPv4 address. */ 1191 pvCopySource = &xDHCPData.ulPreferredIPAddress; 1192 pvCopyDest = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ); 1193 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulPreferredIPAddress ) ); 1194 } 1195 else 1196 { 1197 /* Remove option-50 from the list because it is not used. */ 1198 size_t uxCopyLength; 1199 /* Exclude this line from branch coverage as the not-taken condition will never happen unless the code is modified */ 1200 configASSERT( uxOptionsLength > ( dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ) ); /* LCOV_EXCL_BR_LINE */ 1201 uxCopyLength = uxOptionsLength - ( dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ); 1202 pvCopySource = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ] ); 1203 pvCopyDest = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpOPTION_50_OFFSET ] ); 1204 ( void ) memmove( pvCopyDest, pvCopySource, uxCopyLength ); 1205 /* Send 6 bytes less than foreseen. */ 1206 uxOptionsLength -= dhcpOPTION_50_SIZE; 1207 } 1208 1209 if( FreeRTOS_sendto( xDHCPSocket, 1210 pucUDPPayloadBuffer, 1211 sizeof( DHCPMessage_IPv4_t ) + uxOptionsLength, 1212 FREERTOS_ZERO_COPY, 1213 &( xAddress ), 1214 ( socklen_t ) sizeof( xAddress ) ) == 0 ) 1215 { 1216 /* The packet was not successfully queued for sending and must be 1217 * returned to the stack. */ 1218 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); 1219 } 1220 else 1221 { 1222 xResult = pdTRUE; 1223 } 1224 } 1225 1226 return xResult; 1227 } 1228 /*-----------------------------------------------------------*/ 1229 1230 1231 #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) 1232 1233 /** 1234 * @brief When DHCP has failed, the code can assign a Link-Layer address, and check if 1235 * another device already uses the IP-address. 1236 */ prvPrepareLinkLayerIPLookUp(void)1237 static void prvPrepareLinkLayerIPLookUp( void ) 1238 { 1239 uint8_t ucLinkLayerIPAddress[ 2 ]; 1240 uint32_t ulNumbers[ 2 ]; 1241 1242 /* After DHCP has failed to answer, prepare everything to start 1243 * trying-out LinkLayer IP-addresses, using the random method. */ 1244 EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); 1245 1246 xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); 1247 xApplicationGetRandomNumber( &( ulNumbers[ 1 ] ) ); 1248 ucLinkLayerIPAddress[ 0 ] = ( uint8_t ) 1 + ( uint8_t ) ( ulNumbers[ 0 ] % 0xFDU ); /* get value 1..254 for IP-address 3rd byte of IP address to try. */ 1249 ucLinkLayerIPAddress[ 1 ] = ( uint8_t ) 1 + ( uint8_t ) ( ulNumbers[ 1 ] % 0xFDU ); /* get value 1..254 for IP-address 4th byte of IP address to try. */ 1250 1251 EP_IPv4_SETTINGS.ulGatewayAddress = 0U; 1252 1253 /* prepare xDHCPData with data to test. */ 1254 EP_DHCPData.ulOfferedIPAddress = 1255 FreeRTOS_inet_addr_quick( LINK_LAYER_ADDRESS_0, LINK_LAYER_ADDRESS_1, ucLinkLayerIPAddress[ 0 ], ucLinkLayerIPAddress[ 1 ] ); 1256 1257 EP_DHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; /* don't care about lease time. just put anything. */ 1258 1259 EP_IPv4_SETTINGS.ulNetMask = 1260 FreeRTOS_inet_addr_quick( LINK_LAYER_NETMASK_0, LINK_LAYER_NETMASK_1, LINK_LAYER_NETMASK_2, LINK_LAYER_NETMASK_3 ); 1261 1262 /* DHCP completed. The IP address can now be used, and the 1263 * timer set to the lease timeout time. */ 1264 *( ipLOCAL_IP_ADDRESS_POINTER ) = EP_DHCPData.ulOfferedIPAddress; 1265 1266 /* Setting the 'local' broadcast address, something like 192.168.1.255' */ 1267 EP_IPv4_SETTINGS.ulBroadcastAddress = ( EP_DHCPData.ulOfferedIPAddress & EP_IPv4_SETTINGS.ulNetMask ) | ~EP_IPv4_SETTINGS.ulNetMask; 1268 1269 /* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */ 1270 prvCloseDHCPSocket(); 1271 1272 xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); 1273 EP_DHCPData.xDHCPTxPeriod = pdMS_TO_TICKS( 3000U + ( ulNumbers[ 0 ] & 0x3ffU ) ); /* do ARP test every (3 + 0-1024mS) seconds. */ 1274 1275 xARPHadIPClash = pdFALSE; /* reset flag that shows if have ARP clash. */ 1276 vARPSendGratuitous(); 1277 } 1278 1279 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ 1280 /*-----------------------------------------------------------*/ 1281 1282 #endif /* ipconfigUSE_DHCP != 0 */ 1283