Skip to content

Commit 665b573

Browse files
Protocols will notify when dhcpcd can exit (#536)
* Protocols will notify when dhcpcd can exit DHCPv6 RELEASE requires the addresses to be dropped before a RELEASE message is sent. We now wait for an acknowledgement or a timeout before notifying that DHCPv6 has stopped for the interface. DHCPv4 RELEASE is the other way around, there is no acknowledgement. So we wait for 1 second after sending the message before removing the address and notifying DHCP has stopped for the interface. If we are not releasing then we notify dhcpcd that the protocol has stopped right away when we drop the lease. dhcpcd will exit once there are no running protocols for the interfaces. Fixes #513. Hopefully #535, #519 and #509 as well. Co-authored-by: Sime Zupanovic (EXT) <[email protected]>
1 parent b573b9d commit 665b573

File tree

12 files changed

+267
-93
lines changed

12 files changed

+267
-93
lines changed

hooks/dhcpcd-run-hooks.8.in

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2323
.\" SUCH DAMAGE.
2424
.\"
25-
.Dd October 11, 2024
25+
.Dd October 6, 2025
2626
.Dt DHCPCD-RUN-HOOKS 8
2727
.Os
2828
.Sh NAME
@@ -105,6 +105,8 @@ dhcpcd renewed its lease.
105105
dhcpcd has rebound to a new DHCP server.
106106
.It Dv REBOOT | Dv REBOOT6
107107
dhcpcd successfully requested a lease from a DHCP server.
108+
.It Dv RELEASE | Dv RELEASE6
109+
dhcpcd has released the lease.
108110
.It Dv DELEGATED6
109111
dhcpcd assigned a delegated prefix to the interface.
110112
.It Dv IPV4LL

src/dhcp.c

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,6 +2866,51 @@ dhcp_reboot(struct interface *ifp)
28662866
send_request(ifp);
28672867
}
28682868

2869+
static void
2870+
dhcp_deconfigure(void *arg)
2871+
{
2872+
struct interface *ifp = arg;
2873+
struct dhcp_state *state = D_STATE(ifp);
2874+
struct if_options *ifo = ifp->options;
2875+
const char *reason;
2876+
2877+
#ifdef AUTH
2878+
dhcp_auth_reset(&state->auth);
2879+
#endif
2880+
2881+
if (state->state == DHS_RELEASE)
2882+
reason = "RELEASE";
2883+
else
2884+
reason = state->reason;
2885+
state->state = DHS_NONE;
2886+
free(state->offer);
2887+
state->offer = NULL;
2888+
state->offer_len = 0;
2889+
free(state->old);
2890+
state->old = state->new;
2891+
state->old_len = state->new_len;
2892+
state->new = NULL;
2893+
state->new_len = 0;
2894+
if (ifo->options & DHCPCD_CONFIGURE)
2895+
ipv4_applyaddr(ifp);
2896+
else {
2897+
state->addr = NULL;
2898+
state->added = 0;
2899+
}
2900+
script_runreason(ifp, reason);
2901+
free(state->old);
2902+
state->old = NULL;
2903+
state->old_len = 0;
2904+
state->lease.addr.s_addr = 0;
2905+
ifo->options &= ~(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED);
2906+
2907+
if (ifo->options & DHCPCD_STOPPING) {
2908+
dhcp_free(ifp);
2909+
dhcpcd_dropped(ifp);
2910+
} else
2911+
dhcp_close(ifp);
2912+
}
2913+
28692914
void
28702915
dhcp_drop(struct interface *ifp, const char *reason)
28712916
{
@@ -2876,6 +2921,7 @@ dhcp_drop(struct interface *ifp, const char *reason)
28762921
* but we do have a timeout, so punt it. */
28772922
if (state == NULL || state->state == DHS_NONE) {
28782923
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
2924+
dhcpcd_dropped(ifp);
28792925
return;
28802926
}
28812927

@@ -2886,6 +2932,7 @@ dhcp_drop(struct interface *ifp, const char *reason)
28862932
#ifdef ARPING
28872933
state->arping_index = -1;
28882934
#endif
2935+
state->reason = reason;
28892936

28902937
if (ifo->options & DHCPCD_RELEASE && !(ifo->options & DHCPCD_INFORM)) {
28912938
/* Failure to send the release may cause this function to
@@ -2899,10 +2946,21 @@ dhcp_drop(struct interface *ifp, const char *reason)
28992946
state->new != NULL &&
29002947
state->lease.server.s_addr != INADDR_ANY)
29012948
{
2949+
/* We need to delay removal of the IP address so the
2950+
* message can be sent.
2951+
* Unlike DHCPv6, there is no acknowledgement. */
2952+
const struct timespec delay = {
2953+
.tv_sec = 1,
2954+
};
2955+
29022956
loginfox("%s: releasing lease of %s",
29032957
ifp->name, inet_ntoa(state->lease.addr));
29042958
dhcp_new_xid(ifp);
29052959
send_message(ifp, DHCP_RELEASE, NULL);
2960+
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
2961+
eloop_timeout_add_tv(ifp->ctx->eloop,
2962+
&delay, dhcp_deconfigure, ifp);
2963+
return;
29062964
}
29072965
}
29082966
#ifdef AUTH
@@ -2919,36 +2977,7 @@ dhcp_drop(struct interface *ifp, const char *reason)
29192977
#endif
29202978

29212979
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
2922-
#ifdef AUTH
2923-
dhcp_auth_reset(&state->auth);
2924-
#endif
2925-
2926-
state->state = DHS_NONE;
2927-
free(state->offer);
2928-
state->offer = NULL;
2929-
state->offer_len = 0;
2930-
free(state->old);
2931-
state->old = state->new;
2932-
state->old_len = state->new_len;
2933-
state->new = NULL;
2934-
state->new_len = 0;
2935-
state->reason = reason;
2936-
if (ifo->options & DHCPCD_CONFIGURE)
2937-
ipv4_applyaddr(ifp);
2938-
else {
2939-
state->addr = NULL;
2940-
state->added = 0;
2941-
script_runreason(ifp, state->reason);
2942-
}
2943-
free(state->old);
2944-
state->old = NULL;
2945-
state->old_len = 0;
2946-
state->lease.addr.s_addr = 0;
2947-
ifo->options &= ~(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED);
2948-
2949-
/* Close DHCP ports so a changed interface family is picked
2950-
* up by a new BPF state. */
2951-
dhcp_close(ifp);
2980+
dhcp_deconfigure(ifp);
29522981
}
29532982

29542983
static int
@@ -3108,6 +3137,12 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
31083137
#define IS_STATE_ACTIVE(s) ((s)-state != DHS_NONE && \
31093138
(s)->state != DHS_INIT && (s)->state != DHS_BOUND)
31103139

3140+
/* Don't do anything if the user hasn't configured it. */
3141+
if (ifp->active != IF_ACTIVE_USER ||
3142+
ifp->options->options & DHCPCD_STOPPING ||
3143+
!(ifp->options->options & DHCPCD_DHCP))
3144+
return;
3145+
31113146
if (bootp->op != BOOTREPLY) {
31123147
if (IS_STATE_ACTIVE(state))
31133148
logdebugx("%s: op (%d) is not BOOTREPLY",
@@ -3932,6 +3967,7 @@ dhcp_free(struct interface *ifp)
39323967
free(state->offer);
39333968
free(state->clientid);
39343969
free(state);
3970+
ifp->if_data[IF_DATA_DHCP] = NULL;
39353971
}
39363972

39373973
ctx = ifp->ctx;

src/dhcp6.c

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2110,29 +2110,25 @@ dhcp6_startrelease(struct interface *ifp)
21102110
struct dhcp6_state *state;
21112111

21122112
state = D6_STATE(ifp);
2113-
if (state->state != DH6S_BOUND)
2113+
if (state->state != DH6S_BOUND) {
2114+
dhcp6_finishrelease(ifp);
21142115
return;
2116+
}
21152117

21162118
state->state = DH6S_RELEASE;
21172119
state->RTC = 0;
21182120
state->IMD = REL_MAX_DELAY;
21192121
state->IRT = REL_TIMEOUT;
21202122
state->MRT = REL_MAX_RT;
2121-
/* MRC of REL_MAX_RC is optional in RFC 3315 18.1.6 */
2122-
#if 0
21232123
state->MRC = REL_MAX_RC;
21242124
state->MRCcallback = dhcp6_finishrelease;
2125-
#else
2126-
state->MRC = 0;
2127-
state->MRCcallback = NULL;
2128-
#endif
21292125

2130-
if (dhcp6_makemessage(ifp) == -1)
2126+
if (dhcp6_makemessage(ifp) == -1) {
21312127
logerr("%s: %s", __func__, ifp->name);
2132-
else {
2133-
dhcp6_sendrelease(ifp);
2128+
/* not much we can do apart from finish now */
21342129
dhcp6_finishrelease(ifp);
2135-
}
2130+
} else
2131+
dhcp6_sendrelease(ifp);
21362132
}
21372133

21382134
static int
@@ -3610,6 +3606,11 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
36103606
ifp->name, sfrom);
36113607
dhcp6_fail(ifp, true);
36123608
return;
3609+
case DH6S_RELEASE:
3610+
loginfox("%s: %s acknowledged RELEASE6",
3611+
ifp->name, sfrom);
3612+
dhcp6_finishrelease(ifp);
3613+
return;
36133614
default:
36143615
valid_op = false;
36153616
break;
@@ -4293,6 +4294,7 @@ dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
42934294
free(state);
42944295
ifp->if_data[IF_DATA_DHCP6] = NULL;
42954296
}
4297+
dhcpcd_dropped(ifp);
42964298

42974299
/* If we don't have any more DHCP6 enabled interfaces,
42984300
* close the global socket and release resources */

0 commit comments

Comments
 (0)