xref: /FreeRTOS-Plus-TCP-v3.1.0/source/FreeRTOS_DHCP.c (revision 37bdfe577f3b728058de714e2e747d3c78803f26)
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