xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/release.c (revision 3431:9f2d277dcffa)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*3431Scarlsonj  * Common Development and Distribution License (the "License").
6*3431Scarlsonj  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*3431Scarlsonj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  * DECLINE/RELEASE configuration functionality for the DHCP client.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <sys/types.h>
31*3431Scarlsonj #include <unistd.h>
320Sstevel@tonic-gate #include <string.h>
330Sstevel@tonic-gate #include <netinet/in.h>
340Sstevel@tonic-gate #include <net/if.h>
350Sstevel@tonic-gate #include <netinet/dhcp.h>
36*3431Scarlsonj #include <netinet/dhcp6.h>
370Sstevel@tonic-gate #include <dhcpmsg.h>
380Sstevel@tonic-gate #include <dhcp_hostconf.h>
390Sstevel@tonic-gate 
40*3431Scarlsonj #include "agent.h"
410Sstevel@tonic-gate #include "packet.h"
420Sstevel@tonic-gate #include "interface.h"
430Sstevel@tonic-gate #include "states.h"
440Sstevel@tonic-gate 
45*3431Scarlsonj static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int);
46*3431Scarlsonj 
470Sstevel@tonic-gate /*
48*3431Scarlsonj  * send_declines(): sends a DECLINE message (broadcasted for IPv4) to the
49*3431Scarlsonj  *		    server to indicate a problem with the offered addresses.
50*3431Scarlsonj  *		    The failing addresses are removed from the leases.
510Sstevel@tonic-gate  *
52*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine sending DECLINE
530Sstevel@tonic-gate  *  output: void
540Sstevel@tonic-gate  */
550Sstevel@tonic-gate 
560Sstevel@tonic-gate void
57*3431Scarlsonj send_declines(dhcp_smach_t *dsmp)
580Sstevel@tonic-gate {
590Sstevel@tonic-gate 	dhcp_pkt_t	*dpkt;
60*3431Scarlsonj 	dhcp_lease_t	*dlp, *dlpn;
61*3431Scarlsonj 	uint_t		nlifs;
62*3431Scarlsonj 	dhcp_lif_t	*lif, *lifn;
63*3431Scarlsonj 	boolean_t	got_one;
640Sstevel@tonic-gate 
65*3431Scarlsonj 	/*
66*3431Scarlsonj 	 * Create an empty DECLINE message.  We'll stuff the information into
67*3431Scarlsonj 	 * this message as we find it.
68*3431Scarlsonj 	 */
69*3431Scarlsonj 	if (dsmp->dsm_isv6) {
70*3431Scarlsonj 		if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL)
71*3431Scarlsonj 			return;
72*3431Scarlsonj 		(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
73*3431Scarlsonj 		    dsmp->dsm_serverid, dsmp->dsm_serveridlen);
74*3431Scarlsonj 	} else {
75*3431Scarlsonj 		ipaddr_t serverip;
76*3431Scarlsonj 
77*3431Scarlsonj 		/*
78*3431Scarlsonj 		 * If this ack is from BOOTP, then there's no way to send a
79*3431Scarlsonj 		 * decline.  Note that since we haven't bound yet, we can't
80*3431Scarlsonj 		 * just check the BOOTP flag.
81*3431Scarlsonj 		 */
82*3431Scarlsonj 		if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL)
83*3431Scarlsonj 			return;
84*3431Scarlsonj 
85*3431Scarlsonj 		if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL)
86*3431Scarlsonj 			return;
87*3431Scarlsonj 		IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip);
88*3431Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip);
89*3431Scarlsonj 	}
900Sstevel@tonic-gate 
91*3431Scarlsonj 	/*
92*3431Scarlsonj 	 * Loop over the leases, looking for ones with now-broken LIFs.  Add
93*3431Scarlsonj 	 * each one found to the DECLINE message, and remove it from the list.
94*3431Scarlsonj 	 * Also remove any completely declined leases.
95*3431Scarlsonj 	 */
96*3431Scarlsonj 	got_one = B_FALSE;
97*3431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) {
98*3431Scarlsonj 		dlpn = dlp->dl_next;
99*3431Scarlsonj 		lif = dlp->dl_lifs;
100*3431Scarlsonj 		for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) {
101*3431Scarlsonj 			lifn = lif->lif_next;
102*3431Scarlsonj 			if (lif->lif_declined != NULL) {
103*3431Scarlsonj 				(void) add_pkt_lif(dpkt, lif,
104*3431Scarlsonj 				    DHCPV6_STAT_UNSPECFAIL, lif->lif_declined);
105*3431Scarlsonj 				unplumb_lif(lif);
106*3431Scarlsonj 				got_one = B_TRUE;
107*3431Scarlsonj 			}
108*3431Scarlsonj 		}
109*3431Scarlsonj 		if (dlp->dl_nlifs == 0)
110*3431Scarlsonj 			remove_lease(dlp);
111*3431Scarlsonj 	}
1120Sstevel@tonic-gate 
113*3431Scarlsonj 	if (!got_one)
114*3431Scarlsonj 		return;
115*3431Scarlsonj 
116*3431Scarlsonj 	(void) set_smach_state(dsmp, DECLINING);
1170Sstevel@tonic-gate 
118*3431Scarlsonj 	if (dsmp->dsm_isv6) {
119*3431Scarlsonj 		(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
120*3431Scarlsonj 		    stop_release_decline, DHCPV6_DEC_TIMEOUT, 0);
121*3431Scarlsonj 	} else {
122*3431Scarlsonj 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
123*3431Scarlsonj 
124*3431Scarlsonj 		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL);
125*3431Scarlsonj 	}
1260Sstevel@tonic-gate }
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate /*
1290Sstevel@tonic-gate  * dhcp_release(): sends a RELEASE message to a DHCP server and removes
130*3431Scarlsonj  *		   the all interfaces for the given state machine from DHCP
131*3431Scarlsonj  *		   control.  Called back by script handler.
1320Sstevel@tonic-gate  *
133*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine to send the RELEASE on and remove
134*3431Scarlsonj  *	    void *: an optional text explanation to send with the message
1350Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
1360Sstevel@tonic-gate  */
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate int
139*3431Scarlsonj dhcp_release(dhcp_smach_t *dsmp, void *arg)
1400Sstevel@tonic-gate {
141*3431Scarlsonj 	const char	*msg = arg;
1420Sstevel@tonic-gate 	dhcp_pkt_t	*dpkt;
143*3431Scarlsonj 	dhcp_lease_t	*dlp;
144*3431Scarlsonj 	dhcp_lif_t	*lif;
145*3431Scarlsonj 	ipaddr_t	serverip;
146*3431Scarlsonj 	uint_t		nlifs;
1470Sstevel@tonic-gate 
148*3431Scarlsonj 	if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) ||
149*3431Scarlsonj 	    !check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) {
150*3431Scarlsonj 		ipc_action_finish(dsmp, DHCP_IPC_E_INT);
151*3431Scarlsonj 		return (0);
152*3431Scarlsonj 	}
1530Sstevel@tonic-gate 
154*3431Scarlsonj 	dhcpmsg(MSG_INFO, "releasing leases for state machine %s",
155*3431Scarlsonj 	    dsmp->dsm_name);
156*3431Scarlsonj 	(void) set_smach_state(dsmp, RELEASING);
1570Sstevel@tonic-gate 
158*3431Scarlsonj 	if (dsmp->dsm_isv6) {
159*3431Scarlsonj 		dpkt = init_pkt(dsmp, DHCPV6_MSG_RELEASE);
160*3431Scarlsonj 		(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
161*3431Scarlsonj 		    dsmp->dsm_serverid, dsmp->dsm_serveridlen);
1620Sstevel@tonic-gate 
163*3431Scarlsonj 		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
164*3431Scarlsonj 			lif = dlp->dl_lifs;
165*3431Scarlsonj 			for (nlifs = dlp->dl_nlifs; nlifs > 0;
166*3431Scarlsonj 			    nlifs--, lif = lif->lif_next) {
167*3431Scarlsonj 				(void) add_pkt_lif(dpkt, lif,
168*3431Scarlsonj 				    DHCPV6_STAT_SUCCESS, NULL);
169*3431Scarlsonj 			}
170*3431Scarlsonj 		}
1710Sstevel@tonic-gate 
172*3431Scarlsonj 		/*
173*3431Scarlsonj 		 * Must kill off the leases before attempting to tell the
174*3431Scarlsonj 		 * server.
175*3431Scarlsonj 		 */
176*3431Scarlsonj 		deprecate_leases(dsmp);
1770Sstevel@tonic-gate 
178*3431Scarlsonj 		/*
179*3431Scarlsonj 		 * For DHCPv6, this is a transaction, rather than just a
180*3431Scarlsonj 		 * one-shot message.  When this transaction is done, we'll
181*3431Scarlsonj 		 * finish the invoking async operation.
182*3431Scarlsonj 		 */
183*3431Scarlsonj 		(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
184*3431Scarlsonj 		    stop_release_decline, DHCPV6_REL_TIMEOUT, 0);
185*3431Scarlsonj 	} else {
186*3431Scarlsonj 		if ((dlp = dsmp->dsm_leases) != NULL && dlp->dl_nlifs > 0) {
187*3431Scarlsonj 			dpkt = init_pkt(dsmp, RELEASE);
188*3431Scarlsonj 			if (msg != NULL) {
189*3431Scarlsonj 				(void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
190*3431Scarlsonj 				    strlen(msg) + 1);
191*3431Scarlsonj 			}
192*3431Scarlsonj 			lif = dlp->dl_lifs;
193*3431Scarlsonj 			(void) add_pkt_lif(dpkt, dlp->dl_lifs, 0, NULL);
1940Sstevel@tonic-gate 
195*3431Scarlsonj 			IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip);
196*3431Scarlsonj 			(void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip);
197*3431Scarlsonj 			(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
198*3431Scarlsonj 			(void) send_pkt(dsmp, dpkt, serverip, NULL);
199*3431Scarlsonj 		}
2000Sstevel@tonic-gate 
201*3431Scarlsonj 		/*
202*3431Scarlsonj 		 * XXX this totally sucks, but since udp is best-effort,
203*3431Scarlsonj 		 * without this delay, there's a good chance that the packet
204*3431Scarlsonj 		 * that we just enqueued for sending will get pitched
205*3431Scarlsonj 		 * when we canonize the interface through remove_smach.
206*3431Scarlsonj 		 */
207*3431Scarlsonj 
208*3431Scarlsonj 		(void) usleep(500);
209*3431Scarlsonj 		deprecate_leases(dsmp);
210*3431Scarlsonj 
211*3431Scarlsonj 		finished_smach(dsmp, DHCP_IPC_SUCCESS);
212*3431Scarlsonj 	}
213*3431Scarlsonj 	return (1);
2140Sstevel@tonic-gate }
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate /*
217*3431Scarlsonj  * dhcp_drop(): drops the interface from DHCP control; callback from script
218*3431Scarlsonj  *		handler
2190Sstevel@tonic-gate  *
220*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine dropping leases
221*3431Scarlsonj  *	    void *: unused
2220Sstevel@tonic-gate  *  output: int: always 1
2230Sstevel@tonic-gate  */
2240Sstevel@tonic-gate 
225*3431Scarlsonj /* ARGSUSED1 */
2260Sstevel@tonic-gate int
227*3431Scarlsonj dhcp_drop(dhcp_smach_t *dsmp, void *arg)
2280Sstevel@tonic-gate {
229*3431Scarlsonj 	dhcpmsg(MSG_INFO, "dropping leases for state machine %s",
230*3431Scarlsonj 	    dsmp->dsm_name);
2310Sstevel@tonic-gate 
232*3431Scarlsonj 	if (dsmp->dsm_state == PRE_BOUND || dsmp->dsm_state == BOUND ||
233*3431Scarlsonj 	    dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
234*3431Scarlsonj 		if (dsmp->dsm_dflags & DHCP_IF_BOOTP) {
235*3431Scarlsonj 			dhcpmsg(MSG_INFO,
236*3431Scarlsonj 			    "used bootp; not writing lease file for %s",
237*3431Scarlsonj 			    dsmp->dsm_name);
238*3431Scarlsonj 		} else {
239*3431Scarlsonj 			PKT_LIST *plp[2];
2400Sstevel@tonic-gate 
241*3431Scarlsonj 			plp[0] = dsmp->dsm_ack;
242*3431Scarlsonj 			plp[1] = dsmp->dsm_orig_ack;
243*3431Scarlsonj 			if (write_hostconf(dsmp->dsm_name, plp, 2,
244*3431Scarlsonj 			    monosec_to_time(dsmp->dsm_curstart_monosec),
245*3431Scarlsonj 			    dsmp->dsm_isv6) == -1) {
2460Sstevel@tonic-gate 				dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
2470Sstevel@tonic-gate 				    "not use cached configuration)",
248*3431Scarlsonj 				    ifname_to_hostconf(dsmp->dsm_name,
249*3431Scarlsonj 				    dsmp->dsm_isv6));
250*3431Scarlsonj 			}
2510Sstevel@tonic-gate 		}
2520Sstevel@tonic-gate 	}
253*3431Scarlsonj 	deprecate_leases(dsmp);
254*3431Scarlsonj 	finished_smach(dsmp, DHCP_IPC_SUCCESS);
2550Sstevel@tonic-gate 	return (1);
2560Sstevel@tonic-gate }
257*3431Scarlsonj 
258*3431Scarlsonj /*
259*3431Scarlsonj  * stop_release_decline(): decides when to stop retransmitting RELEASE/DECLINE
260*3431Scarlsonj  *			   messages for DHCPv6.  When we stop, if there are no
261*3431Scarlsonj  *			   more leases left, then restart the state machine.
262*3431Scarlsonj  *
263*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine messages are being sent from
264*3431Scarlsonj  *	    unsigned int: the number of messages sent so far
265*3431Scarlsonj  *  output: boolean_t: B_TRUE if retransmissions should stop
266*3431Scarlsonj  */
267*3431Scarlsonj 
268*3431Scarlsonj static boolean_t
269*3431Scarlsonj stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests)
270*3431Scarlsonj {
271*3431Scarlsonj 	if (dsmp->dsm_state == RELEASING) {
272*3431Scarlsonj 		if (n_requests >= DHCPV6_REL_MAX_RC) {
273*3431Scarlsonj 			dhcpmsg(MSG_INFO, "no Reply to Release, finishing "
274*3431Scarlsonj 			    "transaction on %s", dsmp->dsm_name);
275*3431Scarlsonj 			finished_smach(dsmp, DHCP_IPC_SUCCESS);
276*3431Scarlsonj 			return (B_TRUE);
277*3431Scarlsonj 		} else {
278*3431Scarlsonj 			return (B_FALSE);
279*3431Scarlsonj 		}
280*3431Scarlsonj 	} else {
281*3431Scarlsonj 		if (n_requests >= DHCPV6_DEC_MAX_RC) {
282*3431Scarlsonj 			dhcpmsg(MSG_INFO, "no Reply to Decline on %s",
283*3431Scarlsonj 			    dsmp->dsm_name);
284*3431Scarlsonj 
285*3431Scarlsonj 			if (dsmp->dsm_leases == NULL) {
286*3431Scarlsonj 				dhcpmsg(MSG_VERBOSE, "stop_release_decline: "
287*3431Scarlsonj 				    "%s has no leases left; restarting",
288*3431Scarlsonj 				    dsmp->dsm_name);
289*3431Scarlsonj 				dhcp_restart(dsmp);
290*3431Scarlsonj 			}
291*3431Scarlsonj 			return (B_TRUE);
292*3431Scarlsonj 		} else {
293*3431Scarlsonj 			return (B_FALSE);
294*3431Scarlsonj 		}
295*3431Scarlsonj 	}
296*3431Scarlsonj }
297