xref: /titanic_44/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c (revision ea8dc4b6d2251b437950c0056bc626b311c73c27)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <netinet/in.h>
33 #include <netinet/dhcp.h>
34 #include <netinet/udp.h>
35 #include <netinet/ip_var.h>
36 #include <netinet/udp_var.h>
37 #include <libinetutil.h>
38 #include <dhcpmsg.h>
39 #include <string.h>
40 
41 #include "packet.h"
42 #include "agent.h"
43 #include "script_handler.h"
44 #include "interface.h"
45 #include "states.h"
46 #include "util.h"
47 
48 /*
49  * next_extend_time(): returns the next time an EXTEND request should be sent
50  *
51  *   input: monosec_t: the absolute time when the next state is entered
52  *  output: uint32_t: the number of seconds in the future to send the next
53  *		      EXTEND request
54  */
55 
56 static uint32_t
57 next_extend_time(monosec_t limit_monosec)
58 {
59 	monosec_t	current_monosec = monosec();
60 
61 	if (limit_monosec - current_monosec < DHCP_REBIND_MIN)
62 		return (0);
63 
64 	return ((limit_monosec - current_monosec) / 2);
65 }
66 
67 /*
68  * dhcp_renew(): attempts to renew a DHCP lease
69  *
70  *   input: iu_tq_t *: unused
71  *	    void *: the ifslist to renew the lease on
72  *  output: void
73  */
74 
75 /* ARGSUSED */
76 void
77 dhcp_renew(iu_tq_t *tqp, void *arg)
78 {
79 	struct ifslist *ifsp = (struct ifslist *)arg;
80 	uint32_t	next;
81 
82 
83 	ifsp->if_timer[DHCP_T1_TIMER] = -1;
84 
85 	if (check_ifs(ifsp) == 0) {
86 		(void) release_ifs(ifsp);
87 		return;
88 	}
89 
90 	/*
91 	 * sanity check: don't send packets if we're past t2.
92 	 */
93 
94 	if (monosec() > (ifsp->if_curstart_monosec + ifsp->if_t2))
95 		return;
96 
97 	next = next_extend_time(ifsp->if_curstart_monosec + ifsp->if_t2);
98 
99 	/*
100 	 * if there isn't an async event pending, then try to renew.
101 	 */
102 
103 	if (!async_pending(ifsp))
104 		if (async_start(ifsp, DHCP_EXTEND, B_FALSE) != 0)
105 
106 			/*
107 			 * try to send extend.  if we don't succeed,
108 			 * async_timeout() will clean us up.
109 			 */
110 
111 			(void) dhcp_extending(ifsp);
112 
113 	/*
114 	 * if we're within DHCP_REBIND_MIN seconds of REBINDING, don't
115 	 * reschedule ourselves.
116 	 */
117 
118 	if (next == 0)
119 		return;
120 
121 	/*
122 	 * no big deal if we can't reschedule; we still have the REBIND
123 	 * state to save us.
124 	 */
125 
126 	(void) schedule_ifs_timer(ifsp, DHCP_T1_TIMER, next, dhcp_renew);
127 }
128 
129 /*
130  * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state
131  *
132  *   input: iu_tq_t *: unused
133  *	    void *: the ifslist to renew the lease on
134  *  output: void
135  */
136 
137 /* ARGSUSED */
138 void
139 dhcp_rebind(iu_tq_t *tqp, void *arg)
140 {
141 	struct ifslist *ifsp = (struct ifslist *)arg;
142 	uint32_t	next;
143 
144 	ifsp->if_timer[DHCP_T2_TIMER] = -1;
145 
146 	if (check_ifs(ifsp) == 0) {
147 		(void) release_ifs(ifsp);
148 		return;
149 	}
150 
151 	/*
152 	 * sanity check: don't send packets if we've already expired.
153 	 */
154 
155 	if (monosec() > (ifsp->if_curstart_monosec + ifsp->if_lease))
156 		return;
157 
158 	next = next_extend_time(ifsp->if_curstart_monosec + ifsp->if_lease);
159 
160 	/*
161 	 * if this is our first venture into the REBINDING state, then
162 	 * reset the server address.  we know the renew timer has
163 	 * already been cancelled (or we wouldn't be here).
164 	 */
165 
166 	if (ifsp->if_state == RENEWING) {
167 		ifsp->if_state = REBINDING;
168 		ifsp->if_server.s_addr = htonl(INADDR_BROADCAST);
169 	}
170 
171 	/*
172 	 * if there isn't an async event pending, then try to rebind.
173 	 */
174 
175 	if (!async_pending(ifsp))
176 		if (async_start(ifsp, DHCP_EXTEND, B_FALSE) != 0)
177 
178 			/*
179 			 * try to send extend.  if we don't succeed,
180 			 * async_timeout() will clean us up.
181 			 */
182 
183 			(void) dhcp_extending(ifsp);
184 
185 	/*
186 	 * if we're within DHCP_REBIND_MIN seconds of EXPIRE, don't
187 	 * reschedule ourselves.
188 	 */
189 
190 	if (next == 0) {
191 		dhcpmsg(MSG_WARNING, "dhcp_rebind: lease on %s expires in less "
192 		    "than %i seconds!", ifsp->if_name, DHCP_REBIND_MIN);
193 		return;
194 	}
195 
196 	if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, next, dhcp_rebind) == 0)
197 
198 		/*
199 		 * we'll just end up in dhcp_expire(), but it sure sucks.
200 		 */
201 
202 		dhcpmsg(MSG_CRIT, "dhcp_rebind: cannot reschedule another "
203 		    "rebind attempt; lease may expire for %s", ifsp->if_name);
204 }
205 
206 /*
207  * dhcp_restart(): callback function to script_start
208  *
209  *   input: struct ifslist *: the interface to be restarted
210  *	    const char *: unused
211  *  output: int: always 1
212  */
213 
214 /* ARGSUSED */
215 static int
216 dhcp_restart(struct ifslist *ifsp, const char *msg)
217 {
218 	dhcpmsg(MSG_INFO, "lease expired on %s -- restarting DHCP",
219 	    ifsp->if_name);
220 
221 	/*
222 	 * in the case where the lease is less than DHCP_REBIND_MIN
223 	 * seconds, we will never enter dhcp_renew() and thus the packet
224 	 * counters will not be reset.  in that case, reset them here.
225 	 */
226 
227 	if (ifsp->if_state == BOUND) {
228 		ifsp->if_bad_offers = 0;
229 		ifsp->if_sent	    = 0;
230 		ifsp->if_received   = 0;
231 	}
232 
233 	(void) canonize_ifs(ifsp);
234 
235 	/* reset_ifs() in dhcp_selecting() will clean up any leftover state */
236 	dhcp_selecting(ifsp);
237 	return (1);
238 }
239 
240 /*
241  * dhcp_expire(): expires a lease on a given interface and restarts DHCP
242  *
243  *   input: iu_tq_t *: unused
244  *	    void *: the ifslist to expire the lease on
245  *  output: void
246  */
247 
248 /* ARGSUSED */
249 void
250 dhcp_expire(iu_tq_t *tqp, void *arg)
251 {
252 	struct ifslist	*ifsp = (struct ifslist *)arg;
253 
254 	ifsp->if_timer[DHCP_LEASE_TIMER] = -1;
255 
256 	if (check_ifs(ifsp) == 0) {
257 		(void) release_ifs(ifsp);
258 		return;
259 	}
260 
261 	if (async_pending(ifsp))
262 
263 		if (async_cancel(ifsp) == 0) {
264 
265 			dhcpmsg(MSG_WARNING, "dhcp_expire: cannot cancel "
266 			    "current asynchronous command against %s",
267 			    ifsp->if_name);
268 
269 			/*
270 			 * try to schedule ourselves for callback.
271 			 * we're really situation critical here
272 			 * there's not much hope for us if this fails.
273 			 */
274 
275 			if (iu_schedule_timer(tq, DHCP_EXPIRE_WAIT, dhcp_expire,
276 			    ifsp) != -1) {
277 				hold_ifs(ifsp);
278 				return;
279 			}
280 
281 			dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule "
282 			    "dhcp_expire to get called back, proceeding...");
283 		}
284 
285 	/*
286 	 * just march on if this fails; at worst someone will be able
287 	 * to async_start() while we're actually busy with our own
288 	 * asynchronous transaction.  better than not having a lease.
289 	 */
290 
291 	if (async_start(ifsp, DHCP_START, B_FALSE) == 0)
292 		dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
293 		    "transaction on %s, continuing...", ifsp->if_name);
294 
295 	(void) script_start(ifsp, EVENT_EXPIRE, dhcp_restart, NULL, NULL);
296 }
297 
298 /*
299  * dhcp_extending(): sends a REQUEST to extend a lease on a given interface
300  *		     and registers to receive the ACK/NAK server reply
301  *
302  *   input: struct ifslist *: the interface to send the REQUEST on
303  *  output: int: 1 if the extension request was sent, 0 otherwise
304  */
305 
306 int
307 dhcp_extending(struct ifslist *ifsp)
308 {
309 	dhcp_pkt_t		*dpkt;
310 
311 	if (ifsp->if_state == BOUND) {
312 		ifsp->if_neg_monosec	= monosec();
313 		ifsp->if_state		= RENEWING;
314 		ifsp->if_bad_offers	= 0;
315 		ifsp->if_sent		= 0;
316 		ifsp->if_received	= 0;
317 	}
318 
319 	dhcpmsg(MSG_DEBUG, "dhcp_extending: registering dhcp_acknak on %s",
320 	    ifsp->if_name);
321 
322 	if (register_acknak(ifsp) == 0) {
323 
324 		ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
325 		async_finish(ifsp);
326 
327 		dhcpmsg(MSG_WARNING, "dhcp_extending: cannot register "
328 		    "dhcp_acknak for %s, not sending renew request",
329 		    ifsp->if_name);
330 
331 		return (0);
332 	}
333 
334 	/*
335 	 * assemble DHCPREQUEST message.  The max dhcp message size
336 	 * option is set to the interface max, minus the size of the udp and
337 	 * ip headers.
338 	 */
339 
340 	dpkt = init_pkt(ifsp, REQUEST);
341 	dpkt->pkt->ciaddr.s_addr = ifsp->if_addr.s_addr;
342 
343 	add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
344 			sizeof (struct udpiphdr)));
345 	add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
346 
347 	add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
348 	add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
349 	/*
350 	 * if_reqhost was set for this interface in dhcp_selecting()
351 	 * if the REQUEST_HOSTNAME option was set and a host name was
352 	 * found.
353 	 */
354 	if (ifsp->if_reqhost != NULL) {
355 		add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
356 		    strlen(ifsp->if_reqhost));
357 	}
358 	add_pkt_opt(dpkt, CD_END, NULL, 0);
359 
360 	/*
361 	 * if we can't send the packet, leave the event handler registered
362 	 * anyway, since we're not expecting to get any other types of
363 	 * packets in other than ACKs/NAKs anyway.
364 	 */
365 
366 	return (send_pkt(ifsp, dpkt, ifsp->if_server.s_addr, NULL));
367 }
368