@@ -394,6 +394,64 @@ static void dhcpv4_set_timeout(struct net_if_dhcpv4 *dhcpv4,
394394 k_work_reschedule (& timeout_work , K_NO_WAIT );
395395}
396396
397+ /* Set a new timeout w/o updating base time. Used for RENEWING and REBINDING to
398+ * keep track of T1/T2/lease timeouts.
399+ */
400+ static void dhcpv4_set_timeout_inc (struct net_if_dhcpv4 * dhcpv4 ,
401+ int64_t now , uint32_t timeout )
402+ {
403+ int64_t timeout_ms ;
404+
405+ NET_DBG ("sched timeout dhcvp4=%p timeout=%us" , dhcpv4 , timeout );
406+
407+ timeout_ms = (now - dhcpv4 -> timer_start ) + MSEC_PER_SEC * timeout ;
408+ dhcpv4 -> request_time = (uint32_t )(timeout_ms / MSEC_PER_SEC );
409+ }
410+
411+ static uint32_t dhcpv4_get_timeleft (int64_t start , uint32_t time , int64_t now )
412+ {
413+ int64_t deadline = start + MSEC_PER_SEC * time ;
414+ uint32_t ret = 0U ;
415+
416+ /* If we haven't reached the deadline, calculate the
417+ * rounded-up whole seconds until the deadline.
418+ */
419+ if (deadline > now ) {
420+ ret = (uint32_t )DIV_ROUND_UP (deadline - now , MSEC_PER_SEC );
421+ }
422+
423+ return ret ;
424+ }
425+
426+ static uint32_t dhcpv4_request_timeleft (struct net_if * iface , int64_t now )
427+ {
428+ uint32_t request_time = iface -> config .dhcpv4 .request_time ;
429+
430+ return dhcpv4_get_timeleft (iface -> config .dhcpv4 .timer_start ,
431+ request_time , now );
432+ }
433+
434+ static uint32_t dhcpv4_renewal_timeleft (struct net_if * iface , int64_t now )
435+ {
436+ return dhcpv4_get_timeleft (iface -> config .dhcpv4 .timer_start ,
437+ iface -> config .dhcpv4 .renewal_time ,
438+ now );
439+ }
440+
441+ static uint32_t dhcpv4_rebinding_timeleft (struct net_if * iface , int64_t now )
442+ {
443+ return dhcpv4_get_timeleft (iface -> config .dhcpv4 .timer_start ,
444+ iface -> config .dhcpv4 .rebinding_time ,
445+ now );
446+ }
447+
448+ static uint32_t dhcpv4_lease_timeleft (struct net_if * iface , int64_t now )
449+ {
450+ return dhcpv4_get_timeleft (iface -> config .dhcpv4 .timer_start ,
451+ iface -> config .dhcpv4 .lease_time ,
452+ now );
453+ }
454+
397455/* Must be invoked with lock held */
398456static uint32_t dhcpv4_update_message_timeout (struct net_if_dhcpv4 * dhcpv4 )
399457{
@@ -415,6 +473,59 @@ static uint32_t dhcpv4_update_message_timeout(struct net_if_dhcpv4 *dhcpv4)
415473 return timeout ;
416474}
417475
476+ static uint32_t dhcpv4_calculate_renew_rebind_timeout (uint32_t timeleft )
477+ {
478+ uint32_t timeout ;
479+
480+ /* RFC2131 4.4.5:
481+ * In both RENEWING and REBINDING states, if the client receives no
482+ * response to its DHCPREQUEST message, the client SHOULD wait one-half
483+ * of the remaining time until T2 (in RENEWING state) and one-half of
484+ * the remaining lease time (in REBINDING state), down to a minimum of
485+ * 60 seconds, before retransmitting the DHCPREQUEST message.
486+ */
487+
488+ if (timeleft < DHCPV4_RENEW_REBIND_TIMEOUT_MIN ) {
489+ timeout = timeleft ;
490+ } else if (timeleft / 2U < DHCPV4_RENEW_REBIND_TIMEOUT_MIN ) {
491+ timeout = DHCPV4_RENEW_REBIND_TIMEOUT_MIN ;
492+ } else {
493+ timeout = timeleft / 2U ;
494+ }
495+
496+ return timeout ;
497+ }
498+
499+ static uint32_t dhcpv4_update_renew_timeout (struct net_if * iface )
500+ {
501+ struct net_if_dhcpv4 * dhcpv4 = & iface -> config .dhcpv4 ;
502+ int64_t now = k_uptime_get ();
503+ uint32_t timeout ;
504+
505+ timeout = dhcpv4_calculate_renew_rebind_timeout (
506+ dhcpv4_rebinding_timeleft (iface , now ));
507+
508+ dhcpv4 -> attempts ++ ;
509+ dhcpv4_set_timeout_inc (dhcpv4 , now , timeout );
510+
511+ return timeout ;
512+ }
513+
514+ static uint32_t dhcpv4_update_rebind_timeout (struct net_if * iface )
515+ {
516+ struct net_if_dhcpv4 * dhcpv4 = & iface -> config .dhcpv4 ;
517+ int64_t now = k_uptime_get ();
518+ uint32_t timeout ;
519+
520+ timeout = dhcpv4_calculate_renew_rebind_timeout (
521+ dhcpv4_lease_timeleft (iface , now ));
522+
523+ dhcpv4 -> attempts ++ ;
524+ dhcpv4_set_timeout_inc (dhcpv4 , now , timeout );
525+
526+ return timeout ;
527+ }
528+
418529/* Prepare DHCPv4 Message request and send it to peer.
419530 *
420531 * Returns the number of seconds until the next time-triggered event,
@@ -447,6 +558,7 @@ static uint32_t dhcpv4_send_request(struct net_if *iface)
447558 with_requested_ip = true;
448559 memcpy (& iface -> config .dhcpv4 .request_server_addr , & iface -> config .dhcpv4 .server_id ,
449560 sizeof (struct in_addr ));
561+ timeout = dhcpv4_update_message_timeout (& iface -> config .dhcpv4 );
450562 break ;
451563 case NET_DHCPV4_RENEWING :
452564 /* Since we have an address populate the ciaddr field.
@@ -456,6 +568,7 @@ static uint32_t dhcpv4_send_request(struct net_if *iface)
456568 /* UNICAST the DHCPREQUEST */
457569 src_addr = ciaddr ;
458570 server_addr = & iface -> config .dhcpv4 .server_id ;
571+ timeout = dhcpv4_update_renew_timeout (iface );
459572
460573 /* RFC2131 4.4.5 Client MUST NOT include server
461574 * identifier in the DHCPREQUEST.
@@ -466,12 +579,11 @@ static uint32_t dhcpv4_send_request(struct net_if *iface)
466579 */
467580 ciaddr = & iface -> config .dhcpv4 .requested_ip ;
468581 src_addr = ciaddr ;
582+ timeout = dhcpv4_update_rebind_timeout (iface );
469583
470584 break ;
471585 }
472586
473- timeout = dhcpv4_update_message_timeout (& iface -> config .dhcpv4 );
474-
475587 pkt = dhcpv4_create_message (iface , NET_DHCPV4_MSG_TYPE_REQUEST ,
476588 ciaddr , src_addr , server_addr ,
477589 with_server_id , with_requested_ip );
@@ -559,60 +671,6 @@ static void dhcpv4_enter_selecting(struct net_if *iface)
559671 net_dhcpv4_state_name (iface -> config .dhcpv4 .state ));
560672}
561673
562- static uint32_t dhcpv4_get_timeleft (int64_t start , uint32_t time , int64_t now )
563- {
564- int64_t deadline = start + MSEC_PER_SEC * time ;
565- uint32_t ret = 0U ;
566-
567- /* If we haven't reached the deadline, calculate the
568- * rounded-up whole seconds until the deadline.
569- */
570- if (deadline > now ) {
571- ret = (uint32_t )DIV_ROUND_UP (deadline - now , MSEC_PER_SEC );
572- }
573-
574- return ret ;
575- }
576-
577- static uint32_t dhcpv4_request_timeleft (struct net_if * iface , int64_t now )
578- {
579- uint32_t request_time = iface -> config .dhcpv4 .request_time ;
580-
581- return dhcpv4_get_timeleft (iface -> config .dhcpv4 .timer_start ,
582- request_time , now );
583- }
584-
585- static uint32_t dhcpv4_renewal_timeleft (struct net_if * iface , int64_t now )
586- {
587- uint32_t rem = dhcpv4_get_timeleft (iface -> config .dhcpv4 .timer_start ,
588- iface -> config .dhcpv4 .renewal_time ,
589- now );
590-
591- if (rem == 0U ) {
592- iface -> config .dhcpv4 .state = NET_DHCPV4_RENEWING ;
593- NET_DBG ("enter state=%s" ,
594- net_dhcpv4_state_name (iface -> config .dhcpv4 .state ));
595- iface -> config .dhcpv4 .attempts = 0U ;
596- }
597-
598- return rem ;
599- }
600-
601- static uint32_t dhcpv4_rebinding_timeleft (struct net_if * iface , int64_t now )
602- {
603- uint32_t rem = dhcpv4_get_timeleft (iface -> config .dhcpv4 .timer_start ,
604- iface -> config .dhcpv4 .rebinding_time ,
605- now );
606- if (rem == 0U ) {
607- iface -> config .dhcpv4 .state = NET_DHCPV4_REBINDING ;
608- NET_DBG ("enter state=%s" ,
609- net_dhcpv4_state_name (iface -> config .dhcpv4 .state ));
610- iface -> config .dhcpv4 .attempts = 0U ;
611- }
612-
613- return rem ;
614- }
615-
616674static void dhcpv4_enter_requesting (struct net_if * iface , struct dhcp_msg * msg )
617675{
618676 iface -> config .dhcpv4 .attempts = 0U ;
@@ -629,36 +687,60 @@ static void dhcpv4_enter_requesting(struct net_if *iface, struct dhcp_msg *msg)
629687/* Must be invoked with lock held */
630688static void dhcpv4_enter_bound (struct net_if * iface )
631689{
632- uint32_t renewal_time ;
633- uint32_t rebinding_time ;
690+ struct net_if_dhcpv4 * dhcpv4 = & iface -> config .dhcpv4 ;
634691
635- renewal_time = iface -> config . dhcpv4 . renewal_time ;
636- if (! renewal_time ) {
692+ /* Load defaults in case server did not provide T1/T2 values. */
693+ if (dhcpv4 -> renewal_time == 0U ) {
637694 /* The default renewal time rfc2131 4.4.5 */
638- renewal_time = iface -> config .dhcpv4 .lease_time / 2U ;
639- iface -> config .dhcpv4 .renewal_time = renewal_time ;
695+ dhcpv4 -> renewal_time = dhcpv4 -> lease_time / 2U ;
640696 }
641697
642- rebinding_time = iface -> config .dhcpv4 .rebinding_time ;
643- if (!rebinding_time ) {
698+ if (dhcpv4 -> rebinding_time == 0U ) {
644699 /* The default rebinding time rfc2131 4.4.5 */
645- rebinding_time = iface -> config .dhcpv4 .lease_time * 875U / 1000 ;
646- iface -> config .dhcpv4 .rebinding_time = rebinding_time ;
700+ dhcpv4 -> rebinding_time = dhcpv4 -> lease_time * 875U / 1000U ;
701+ }
702+
703+ /* RFC2131 4.4.5:
704+ * T1 MUST be earlier than T2, which, in turn, MUST be earlier than the
705+ * time at which the client's lease will expire.
706+ */
707+ if ((dhcpv4 -> renewal_time >= dhcpv4 -> rebinding_time ) ||
708+ (dhcpv4 -> rebinding_time >= dhcpv4 -> lease_time )) {
709+ /* In case server did not provide valid values, fall back to
710+ * defaults.
711+ */
712+ dhcpv4 -> renewal_time = dhcpv4 -> lease_time / 2U ;
713+ dhcpv4 -> rebinding_time = dhcpv4 -> lease_time * 875U / 1000U ;
647714 }
648715
649- iface -> config . dhcpv4 . state = NET_DHCPV4_BOUND ;
716+ dhcpv4 -> state = NET_DHCPV4_BOUND ;
650717 NET_DBG ("enter state=%s renewal=%us rebinding=%us" ,
651- net_dhcpv4_state_name (iface -> config . dhcpv4 . state ),
652- renewal_time , rebinding_time );
718+ net_dhcpv4_state_name (dhcpv4 -> state ),
719+ dhcpv4 -> renewal_time , dhcpv4 -> rebinding_time );
653720
654- dhcpv4_set_timeout (& iface -> config .dhcpv4 ,
655- MIN (renewal_time , rebinding_time ));
721+ dhcpv4_set_timeout (dhcpv4 , dhcpv4 -> renewal_time );
656722
657723 net_mgmt_event_notify_with_info (NET_EVENT_IPV4_DHCP_BOUND , iface ,
658724 & iface -> config .dhcpv4 ,
659725 sizeof (iface -> config .dhcpv4 ));
660726}
661727
728+ static void dhcpv4_enter_renewing (struct net_if * iface )
729+ {
730+ iface -> config .dhcpv4 .state = NET_DHCPV4_RENEWING ;
731+ iface -> config .dhcpv4 .attempts = 0U ;
732+ NET_DBG ("enter state=%s" ,
733+ net_dhcpv4_state_name (iface -> config .dhcpv4 .state ));
734+ }
735+
736+ static void dhcpv4_enter_rebinding (struct net_if * iface )
737+ {
738+ iface -> config .dhcpv4 .state = NET_DHCPV4_REBINDING ;
739+ iface -> config .dhcpv4 .attempts = 0U ;
740+ NET_DBG ("enter state=%s" ,
741+ net_dhcpv4_state_name (iface -> config .dhcpv4 .state ));
742+ }
743+
662744static uint32_t dhcpv4_manage_timers (struct net_if * iface , int64_t now )
663745{
664746 uint32_t timeleft = dhcpv4_request_timeleft (iface , now );
@@ -701,34 +783,34 @@ static uint32_t dhcpv4_manage_timers(struct net_if *iface, int64_t now)
701783 return dhcpv4_send_request (iface );
702784 case NET_DHCPV4_BOUND :
703785 timeleft = dhcpv4_renewal_timeleft (iface , now );
704- if (timeleft != 0U ) {
705- timeleft = MIN (timeleft ,
706- dhcpv4_rebinding_timeleft (iface , now ));
707- }
708786 if (timeleft == 0U ) {
787+ dhcpv4_enter_renewing (iface );
709788 return dhcpv4_send_request (iface );
710789 }
711790
712791 return timeleft ;
713792 case NET_DHCPV4_RENEWING :
714- case NET_DHCPV4_REBINDING :
715- if (iface -> config . dhcpv4 . attempts >=
716- DHCPV4_MAX_NUMBER_OF_ATTEMPTS ) {
717- NET_DBG ( "too many attempts, restart" );
793+ timeleft = dhcpv4_rebinding_timeleft ( iface , now );
794+ if (timeleft == 0U ) {
795+ dhcpv4_enter_rebinding ( iface );
796+ }
718797
719- if (!net_if_ipv4_addr_rm (iface ,
720- & iface -> config .dhcpv4 .requested_ip )) {
798+ return dhcpv4_send_request (iface );
799+ case NET_DHCPV4_REBINDING :
800+ timeleft = dhcpv4_lease_timeleft (iface , now );
801+ if (timeleft == 0U ) {
802+ if (!net_if_ipv4_addr_rm (
803+ iface ,
804+ & iface -> config .dhcpv4 .requested_ip )) {
721805 NET_DBG ("Failed to remove addr from iface" );
722806 }
723807
724- /* Maximum number of renewal attempts failed, so start
725- * from the beginning.
726- */
808+ /* Lease time expired, so start from the beginning. */
727809 dhcpv4_enter_selecting (iface );
728810 return dhcpv4_send_discover (iface );
729- } else {
730- return dhcpv4_send_request (iface );
731811 }
812+
813+ return dhcpv4_send_request (iface );
732814 }
733815
734816 return UINT32_MAX ;
0 commit comments