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