17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5d04ccbb3Scarlsonj  * Common Development and Distribution License (the "License").
6d04ccbb3Scarlsonj  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*0a3e1f6cSVasumathi Sundaram  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  *
247c478bd9Sstevel@tonic-gate  * DECLINE/RELEASE configuration functionality for the DHCP client.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
28d04ccbb3Scarlsonj #include <unistd.h>
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <netinet/in.h>
317c478bd9Sstevel@tonic-gate #include <net/if.h>
327c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h>
33d04ccbb3Scarlsonj #include <netinet/dhcp6.h>
347c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
357c478bd9Sstevel@tonic-gate #include <dhcp_hostconf.h>
36cfb9c9abScarlsonj #include <dhcpagent_util.h>
377c478bd9Sstevel@tonic-gate 
38d04ccbb3Scarlsonj #include "agent.h"
397c478bd9Sstevel@tonic-gate #include "packet.h"
407c478bd9Sstevel@tonic-gate #include "interface.h"
417c478bd9Sstevel@tonic-gate #include "states.h"
427c478bd9Sstevel@tonic-gate 
43d04ccbb3Scarlsonj static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int);
44d04ccbb3Scarlsonj 
457c478bd9Sstevel@tonic-gate /*
46d04ccbb3Scarlsonj  * send_declines(): sends a DECLINE message (broadcasted for IPv4) to the
47d04ccbb3Scarlsonj  *		    server to indicate a problem with the offered addresses.
48d04ccbb3Scarlsonj  *		    The failing addresses are removed from the leases.
497c478bd9Sstevel@tonic-gate  *
50d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine sending DECLINE
517c478bd9Sstevel@tonic-gate  *  output: void
527c478bd9Sstevel@tonic-gate  */
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate void
send_declines(dhcp_smach_t * dsmp)55d04ccbb3Scarlsonj send_declines(dhcp_smach_t *dsmp)
567c478bd9Sstevel@tonic-gate {
577c478bd9Sstevel@tonic-gate 	dhcp_pkt_t	*dpkt;
58d04ccbb3Scarlsonj 	dhcp_lease_t	*dlp, *dlpn;
59d04ccbb3Scarlsonj 	uint_t		nlifs;
60d04ccbb3Scarlsonj 	dhcp_lif_t	*lif, *lifn;
61d04ccbb3Scarlsonj 	boolean_t	got_one;
627c478bd9Sstevel@tonic-gate 
63d04ccbb3Scarlsonj 	/*
64d04ccbb3Scarlsonj 	 * Create an empty DECLINE message.  We'll stuff the information into
65d04ccbb3Scarlsonj 	 * this message as we find it.
66d04ccbb3Scarlsonj 	 */
67d04ccbb3Scarlsonj 	if (dsmp->dsm_isv6) {
68d04ccbb3Scarlsonj 		if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL)
69d04ccbb3Scarlsonj 			return;
70d04ccbb3Scarlsonj 		(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
71d04ccbb3Scarlsonj 		    dsmp->dsm_serverid, dsmp->dsm_serveridlen);
72d04ccbb3Scarlsonj 	} else {
73d04ccbb3Scarlsonj 		ipaddr_t serverip;
747c478bd9Sstevel@tonic-gate 
75d04ccbb3Scarlsonj 		/*
76d04ccbb3Scarlsonj 		 * If this ack is from BOOTP, then there's no way to send a
77d04ccbb3Scarlsonj 		 * decline.  Note that since we haven't bound yet, we can't
78d04ccbb3Scarlsonj 		 * just check the BOOTP flag.
79d04ccbb3Scarlsonj 		 */
80d04ccbb3Scarlsonj 		if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL)
81d04ccbb3Scarlsonj 			return;
827c478bd9Sstevel@tonic-gate 
83d04ccbb3Scarlsonj 		if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL)
84d04ccbb3Scarlsonj 			return;
85d04ccbb3Scarlsonj 		IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip);
86d04ccbb3Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip);
87d04ccbb3Scarlsonj 	}
887c478bd9Sstevel@tonic-gate 
89d04ccbb3Scarlsonj 	/*
90d04ccbb3Scarlsonj 	 * Loop over the leases, looking for ones with now-broken LIFs.  Add
91d04ccbb3Scarlsonj 	 * each one found to the DECLINE message, and remove it from the list.
92d04ccbb3Scarlsonj 	 * Also remove any completely declined leases.
93d04ccbb3Scarlsonj 	 */
94d04ccbb3Scarlsonj 	got_one = B_FALSE;
95d04ccbb3Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) {
96d04ccbb3Scarlsonj 		dlpn = dlp->dl_next;
97d04ccbb3Scarlsonj 		lif = dlp->dl_lifs;
98d04ccbb3Scarlsonj 		for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) {
99d04ccbb3Scarlsonj 			lifn = lif->lif_next;
100d04ccbb3Scarlsonj 			if (lif->lif_declined != NULL) {
101d04ccbb3Scarlsonj 				(void) add_pkt_lif(dpkt, lif,
102d04ccbb3Scarlsonj 				    DHCPV6_STAT_UNSPECFAIL, lif->lif_declined);
103d04ccbb3Scarlsonj 				unplumb_lif(lif);
104d04ccbb3Scarlsonj 				got_one = B_TRUE;
105d04ccbb3Scarlsonj 			}
106d04ccbb3Scarlsonj 		}
107d04ccbb3Scarlsonj 		if (dlp->dl_nlifs == 0)
108d04ccbb3Scarlsonj 			remove_lease(dlp);
109d04ccbb3Scarlsonj 	}
110d04ccbb3Scarlsonj 
111d04ccbb3Scarlsonj 	if (!got_one)
112d04ccbb3Scarlsonj 		return;
113d04ccbb3Scarlsonj 
114d04ccbb3Scarlsonj 	(void) set_smach_state(dsmp, DECLINING);
115d04ccbb3Scarlsonj 
116d04ccbb3Scarlsonj 	if (dsmp->dsm_isv6) {
117d04ccbb3Scarlsonj 		(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
118d04ccbb3Scarlsonj 		    stop_release_decline, DHCPV6_DEC_TIMEOUT, 0);
119d04ccbb3Scarlsonj 	} else {
120d04ccbb3Scarlsonj 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
121d04ccbb3Scarlsonj 
122d04ccbb3Scarlsonj 		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL);
123d04ccbb3Scarlsonj 	}
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate /*
1277c478bd9Sstevel@tonic-gate  * dhcp_release(): sends a RELEASE message to a DHCP server and removes
128d04ccbb3Scarlsonj  *		   the all interfaces for the given state machine from DHCP
129d04ccbb3Scarlsonj  *		   control.  Called back by script handler.
1307c478bd9Sstevel@tonic-gate  *
131d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to send the RELEASE on and remove
132d04ccbb3Scarlsonj  *	    void *: an optional text explanation to send with the message
1337c478bd9Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
1347c478bd9Sstevel@tonic-gate  */
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate int
dhcp_release(dhcp_smach_t * dsmp,void * arg)137d04ccbb3Scarlsonj dhcp_release(dhcp_smach_t *dsmp, void *arg)
1387c478bd9Sstevel@tonic-gate {
139d04ccbb3Scarlsonj 	const char	*msg = arg;
1407c478bd9Sstevel@tonic-gate 	dhcp_pkt_t	*dpkt;
141d04ccbb3Scarlsonj 	dhcp_lease_t	*dlp;
142d04ccbb3Scarlsonj 	dhcp_lif_t	*lif;
143d04ccbb3Scarlsonj 	ipaddr_t	serverip;
144d04ccbb3Scarlsonj 	uint_t		nlifs;
1457c478bd9Sstevel@tonic-gate 
146d04ccbb3Scarlsonj 	if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) ||
147d04ccbb3Scarlsonj 	    !check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) {
148d04ccbb3Scarlsonj 		ipc_action_finish(dsmp, DHCP_IPC_E_INT);
149d04ccbb3Scarlsonj 		return (0);
150d04ccbb3Scarlsonj 	}
1517c478bd9Sstevel@tonic-gate 
152d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO, "releasing leases for state machine %s",
153d04ccbb3Scarlsonj 	    dsmp->dsm_name);
154d04ccbb3Scarlsonj 	(void) set_smach_state(dsmp, RELEASING);
1557c478bd9Sstevel@tonic-gate 
156*0a3e1f6cSVasumathi Sundaram 	(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
157*0a3e1f6cSVasumathi Sundaram 
158d04ccbb3Scarlsonj 	if (dsmp->dsm_isv6) {
159d04ccbb3Scarlsonj 		dpkt = init_pkt(dsmp, DHCPV6_MSG_RELEASE);
160d04ccbb3Scarlsonj 		(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
161d04ccbb3Scarlsonj 		    dsmp->dsm_serverid, dsmp->dsm_serveridlen);
1627c478bd9Sstevel@tonic-gate 
163d04ccbb3Scarlsonj 		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
164d04ccbb3Scarlsonj 			lif = dlp->dl_lifs;
165d04ccbb3Scarlsonj 			for (nlifs = dlp->dl_nlifs; nlifs > 0;
166d04ccbb3Scarlsonj 			    nlifs--, lif = lif->lif_next) {
167d04ccbb3Scarlsonj 				(void) add_pkt_lif(dpkt, lif,
168d04ccbb3Scarlsonj 				    DHCPV6_STAT_SUCCESS, NULL);
169d04ccbb3Scarlsonj 			}
170d04ccbb3Scarlsonj 		}
1717c478bd9Sstevel@tonic-gate 
172d04ccbb3Scarlsonj 		/*
173d04ccbb3Scarlsonj 		 * Must kill off the leases before attempting to tell the
174d04ccbb3Scarlsonj 		 * server.
175d04ccbb3Scarlsonj 		 */
176d04ccbb3Scarlsonj 		deprecate_leases(dsmp);
1777c478bd9Sstevel@tonic-gate 
178d04ccbb3Scarlsonj 		/*
179d04ccbb3Scarlsonj 		 * For DHCPv6, this is a transaction, rather than just a
180d04ccbb3Scarlsonj 		 * one-shot message.  When this transaction is done, we'll
181d04ccbb3Scarlsonj 		 * finish the invoking async operation.
182d04ccbb3Scarlsonj 		 */
183d04ccbb3Scarlsonj 		(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
184d04ccbb3Scarlsonj 		    stop_release_decline, DHCPV6_REL_TIMEOUT, 0);
185d04ccbb3Scarlsonj 	} else {
186d04ccbb3Scarlsonj 		if ((dlp = dsmp->dsm_leases) != NULL && dlp->dl_nlifs > 0) {
187d04ccbb3Scarlsonj 			dpkt = init_pkt(dsmp, RELEASE);
188d04ccbb3Scarlsonj 			if (msg != NULL) {
189d04ccbb3Scarlsonj 				(void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
190d04ccbb3Scarlsonj 				    strlen(msg) + 1);
191d04ccbb3Scarlsonj 			}
192d04ccbb3Scarlsonj 			lif = dlp->dl_lifs;
193d04ccbb3Scarlsonj 			(void) add_pkt_lif(dpkt, dlp->dl_lifs, 0, NULL);
1947c478bd9Sstevel@tonic-gate 
195d04ccbb3Scarlsonj 			IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip);
196d04ccbb3Scarlsonj 			(void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip);
197d04ccbb3Scarlsonj 			(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
198d04ccbb3Scarlsonj 			(void) send_pkt(dsmp, dpkt, serverip, NULL);
199d04ccbb3Scarlsonj 		}
2007c478bd9Sstevel@tonic-gate 
201d04ccbb3Scarlsonj 		/*
202d04ccbb3Scarlsonj 		 * XXX this totally sucks, but since udp is best-effort,
203d04ccbb3Scarlsonj 		 * without this delay, there's a good chance that the packet
204d04ccbb3Scarlsonj 		 * that we just enqueued for sending will get pitched
205d04ccbb3Scarlsonj 		 * when we canonize the interface through remove_smach.
206d04ccbb3Scarlsonj 		 */
2077c478bd9Sstevel@tonic-gate 
208d04ccbb3Scarlsonj 		(void) usleep(500);
209d04ccbb3Scarlsonj 		deprecate_leases(dsmp);
2107c478bd9Sstevel@tonic-gate 
211d04ccbb3Scarlsonj 		finished_smach(dsmp, DHCP_IPC_SUCCESS);
212d04ccbb3Scarlsonj 	}
213d04ccbb3Scarlsonj 	return (1);
2147c478bd9Sstevel@tonic-gate }
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate /*
217d04ccbb3Scarlsonj  * dhcp_drop(): drops the interface from DHCP control; callback from script
218d04ccbb3Scarlsonj  *		handler
2197c478bd9Sstevel@tonic-gate  *
220d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine dropping leases
221d04ccbb3Scarlsonj  *	    void *: unused
2227c478bd9Sstevel@tonic-gate  *  output: int: always 1
2237c478bd9Sstevel@tonic-gate  */
2247c478bd9Sstevel@tonic-gate 
225d04ccbb3Scarlsonj /* ARGSUSED1 */
2267c478bd9Sstevel@tonic-gate int
dhcp_drop(dhcp_smach_t * dsmp,void * arg)227d04ccbb3Scarlsonj dhcp_drop(dhcp_smach_t *dsmp, void *arg)
2287c478bd9Sstevel@tonic-gate {
229d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO, "dropping leases for state machine %s",
230d04ccbb3Scarlsonj 	    dsmp->dsm_name);
2317c478bd9Sstevel@tonic-gate 
232d04ccbb3Scarlsonj 	if (dsmp->dsm_state == PRE_BOUND || dsmp->dsm_state == BOUND ||
233d04ccbb3Scarlsonj 	    dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
234d04ccbb3Scarlsonj 		if (dsmp->dsm_dflags & DHCP_IF_BOOTP) {
235d04ccbb3Scarlsonj 			dhcpmsg(MSG_INFO,
236d04ccbb3Scarlsonj 			    "used bootp; not writing lease file for %s",
237d04ccbb3Scarlsonj 			    dsmp->dsm_name);
238d04ccbb3Scarlsonj 		} else {
239*0a3e1f6cSVasumathi Sundaram 			write_lease_to_hostconf(dsmp);
2407c478bd9Sstevel@tonic-gate 		}
241cfb9c9abScarlsonj 	} else {
242cfb9c9abScarlsonj 		dhcpmsg(MSG_DEBUG, "%s in state %s; not saving lease",
243cfb9c9abScarlsonj 		    dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
2447c478bd9Sstevel@tonic-gate 	}
245d04ccbb3Scarlsonj 	deprecate_leases(dsmp);
246d04ccbb3Scarlsonj 	finished_smach(dsmp, DHCP_IPC_SUCCESS);
2477c478bd9Sstevel@tonic-gate 	return (1);
2487c478bd9Sstevel@tonic-gate }
249d04ccbb3Scarlsonj 
250d04ccbb3Scarlsonj /*
251d04ccbb3Scarlsonj  * stop_release_decline(): decides when to stop retransmitting RELEASE/DECLINE
252d04ccbb3Scarlsonj  *			   messages for DHCPv6.  When we stop, if there are no
253d04ccbb3Scarlsonj  *			   more leases left, then restart the state machine.
254d04ccbb3Scarlsonj  *
255d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine messages are being sent from
256d04ccbb3Scarlsonj  *	    unsigned int: the number of messages sent so far
257d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE if retransmissions should stop
258d04ccbb3Scarlsonj  */
259d04ccbb3Scarlsonj 
260d04ccbb3Scarlsonj static boolean_t
stop_release_decline(dhcp_smach_t * dsmp,unsigned int n_requests)261d04ccbb3Scarlsonj stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests)
262d04ccbb3Scarlsonj {
263d04ccbb3Scarlsonj 	if (dsmp->dsm_state == RELEASING) {
264d04ccbb3Scarlsonj 		if (n_requests >= DHCPV6_REL_MAX_RC) {
265d04ccbb3Scarlsonj 			dhcpmsg(MSG_INFO, "no Reply to Release, finishing "
266d04ccbb3Scarlsonj 			    "transaction on %s", dsmp->dsm_name);
267d04ccbb3Scarlsonj 			finished_smach(dsmp, DHCP_IPC_SUCCESS);
268d04ccbb3Scarlsonj 			return (B_TRUE);
269d04ccbb3Scarlsonj 		} else {
270d04ccbb3Scarlsonj 			return (B_FALSE);
271d04ccbb3Scarlsonj 		}
272d04ccbb3Scarlsonj 	} else {
273d04ccbb3Scarlsonj 		if (n_requests >= DHCPV6_DEC_MAX_RC) {
274d04ccbb3Scarlsonj 			dhcpmsg(MSG_INFO, "no Reply to Decline on %s",
275d04ccbb3Scarlsonj 			    dsmp->dsm_name);
276d04ccbb3Scarlsonj 
277d04ccbb3Scarlsonj 			if (dsmp->dsm_leases == NULL) {
278d04ccbb3Scarlsonj 				dhcpmsg(MSG_VERBOSE, "stop_release_decline: "
279906cb642Scarlsonj 				    "%s has no leases left", dsmp->dsm_name);
280d04ccbb3Scarlsonj 				dhcp_restart(dsmp);
281d04ccbb3Scarlsonj 			}
282d04ccbb3Scarlsonj 			return (B_TRUE);
283d04ccbb3Scarlsonj 		} else {
284d04ccbb3Scarlsonj 			return (B_FALSE);
285d04ccbb3Scarlsonj 		}
286d04ccbb3Scarlsonj 	}
287d04ccbb3Scarlsonj }
288