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