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  * BOUND state of the DHCP client state machine.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/socket.h>
31 #include <sys/types.h>
32 #include <string.h>
33 #include <netinet/in.h>
34 #include <sys/sockio.h>
35 #include <unistd.h>
36 #include <time.h>
37 #include <arpa/inet.h>
38 #include <stdlib.h>
39 #include <sys/sysmacros.h>
40 #include <dhcp_hostconf.h>
41 #include <dhcpmsg.h>
42 
43 #include "states.h"
44 #include "packet.h"
45 #include "util.h"
46 #include "agent.h"
47 #include "interface.h"
48 #include "script_handler.h"
49 
50 #define	IS_DHCP(plp)	((plp)->opts[CD_DHCP_TYPE] != NULL)
51 
52 static int	configure_if(struct ifslist *);
53 static int	configure_bound(struct ifslist *);
54 static int	configure_timers(struct ifslist *);
55 
56 /*
57  * bound_event_cb(): callback for script_start on the event EVENT_BOUND
58  *
59  *   input: struct ifslist *: the interface configured
60  *	    const char *: unused
61  *  output: int: always 1
62  */
63 
64 /* ARGSUSED */
65 static int
66 bound_event_cb(struct ifslist *ifsp, const char *msg)
67 {
68 	ipc_action_finish(ifsp, DHCP_IPC_SUCCESS);
69 	async_finish(ifsp);
70 	return (1);
71 }
72 
73 /*
74  * dhcp_bound(): configures an interface and ifs using information contained
75  *		 in the ACK packet and sets up lease timers.  before starting,
76  *		 the requested address is arped to make sure it's not in use.
77  *
78  *   input: struct ifslist *: the interface to move to bound
79  *	    PKT_LIST *: the ACK packet, or NULL if it should use ifsp->if_ack
80  *  output: int: 0 on failure, 1 on success
81  */
82 
83 int
84 dhcp_bound(struct ifslist *ifsp, PKT_LIST *ack)
85 {
86 	lease_t		cur_lease, new_lease;
87 	int		msg_level;
88 	const char	*noext = "lease renewed but time not extended";
89 	uint_t		minleft;
90 
91 	if (ack != NULL) {
92 		/* If ack we're replacing is not the original, then free it */
93 		if (ifsp->if_ack != ifsp->if_orig_ack)
94 			free_pkt_list(&ifsp->if_ack);
95 		ifsp->if_ack = ack;
96 		/* Save the first ack as the original */
97 		if (ifsp->if_orig_ack == NULL)
98 			ifsp->if_orig_ack = ack;
99 	}
100 
101 	switch (ifsp->if_state) {
102 
103 	case ADOPTING:
104 
105 		/*
106 		 * if we're adopting an interface, the lease timers
107 		 * only provide an upper bound since we don't know
108 		 * from what time they are relative to.  assume we
109 		 * have a lease time of at most DHCP_ADOPT_LEASE_MAX.
110 		 */
111 
112 		if (!IS_DHCP(ifsp->if_ack))
113 			return (0);
114 
115 		(void) memcpy(&new_lease,
116 		    ifsp->if_ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
117 
118 		new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX));
119 
120 		(void) memcpy(ifsp->if_ack->opts[CD_LEASE_TIME]->value,
121 		    &new_lease, sizeof (lease_t));
122 
123 		if (configure_bound(ifsp) == 0)
124 			return (0);
125 
126 		/*
127 		 * we have no idea when the REQUEST that generated
128 		 * this ACK was sent, but for diagnostic purposes
129 		 * we'll assume its close to the current time.
130 		 */
131 		ifsp->if_newstart_monosec = monosec();
132 
133 		if (configure_timers(ifsp) == 0)
134 			return (0);
135 
136 		/*
137 		 * if the state is ADOPTING, event loop has not been started
138 		 * at this time; so don't run the EVENT_BOUND script.
139 		 */
140 		ifsp->if_curstart_monosec = ifsp->if_newstart_monosec;
141 		ifsp->if_state = BOUND;
142 		break;
143 
144 	case REQUESTING:
145 	case INIT_REBOOT:
146 
147 		if (configure_if(ifsp) == 0)
148 			return (0);
149 
150 		if (configure_timers(ifsp) == 0)
151 			return (0);
152 
153 		/*
154 		 * We will continue configuring this interface via
155 		 * dhcp_bound_complete, once kernel DAD completes.
156 		 */
157 		ifsp->if_state = PRE_BOUND;
158 		break;
159 
160 	case PRE_BOUND:
161 		/* This is just a duplicate ack; silently ignore it */
162 		return (1);
163 
164 	case RENEWING:
165 	case REBINDING:
166 	case BOUND:
167 		cur_lease = ifsp->if_lease;
168 		if (configure_timers(ifsp) == 0)
169 			return (0);
170 
171 		/*
172 		 * if the current lease is mysteriously close to the new
173 		 * lease, warn the user.  unless there's less than a minute
174 		 * left, round to the closest minute.
175 		 */
176 
177 		if (abs((ifsp->if_newstart_monosec + ifsp->if_lease) -
178 		    (ifsp->if_curstart_monosec + cur_lease)) < DHCP_LEASE_EPS) {
179 
180 			if (ifsp->if_lease < DHCP_LEASE_ERROR_THRESH)
181 				msg_level = MSG_ERROR;
182 			else
183 				msg_level = MSG_VERBOSE;
184 
185 			minleft = (ifsp->if_lease + 30) / 60;
186 
187 			if (ifsp->if_lease < 60) {
188 				dhcpmsg(msg_level, "%s; expires in %d seconds",
189 				    noext, ifsp->if_lease);
190 			} else if (minleft == 1) {
191 				dhcpmsg(msg_level, "%s; expires in 1 minute",
192 				    noext);
193 			} else {
194 				dhcpmsg(msg_level, "%s; expires in %d minutes",
195 				    noext, minleft);
196 			}
197 		}
198 
199 		(void) script_start(ifsp, EVENT_EXTEND, bound_event_cb,
200 		    NULL, NULL);
201 
202 		ifsp->if_state = BOUND;
203 		ifsp->if_curstart_monosec = ifsp->if_newstart_monosec;
204 		break;
205 
206 	case INFORM_SENT:
207 
208 		(void) bound_event_cb(ifsp, NULL);
209 		ifsp->if_state = INFORMATION;
210 		break;
211 
212 	default:
213 		/* something is really bizarre... */
214 		dhcpmsg(MSG_DEBUG, "dhcp_bound: called in unexpected state");
215 		return (0);
216 	}
217 
218 	/*
219 	 * remove any stale hostconf file that might be lying around for
220 	 * this interface. (in general, it's harmless, since we'll write a
221 	 * fresh one when we exit anyway, but just to reduce confusion..)
222 	 */
223 
224 	(void) remove_hostconf(ifsp->if_name);
225 	return (1);
226 }
227 
228 /*
229  * dhcp_bound_complete(): complete interface configuration after DAD
230  *
231  *   input: struct ifslist *: the interface to configure
232  *  output: none
233  */
234 
235 void
236 dhcp_bound_complete(struct ifslist *ifsp)
237 {
238 	if (configure_bound(ifsp) == 0)
239 		return;
240 
241 	(void) script_start(ifsp, EVENT_BOUND, bound_event_cb, NULL, NULL);
242 
243 	ifsp->if_state = BOUND;
244 	ifsp->if_curstart_monosec = ifsp->if_newstart_monosec;
245 }
246 
247 /*
248  * configure_timers(): configures the lease timers on an interface
249  *
250  *   input: struct ifslist *: the interface to configure (with a valid if_ack)
251  *  output: int: 1 on success, 0 on failure
252  */
253 
254 static int
255 configure_timers(struct ifslist *ifsp)
256 {
257 	lease_t		lease, t1, t2;
258 
259 	if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL &&
260 	    (ifsp->if_ack->opts[CD_LEASE_TIME] == NULL ||
261 	    ifsp->if_ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
262 		send_decline(ifsp, "Missing or corrupted lease time",
263 		    &ifsp->if_ack->pkt->yiaddr);
264 		dhcpmsg(MSG_WARNING, "configure_timers: missing or corrupted "
265 		    "lease time in ACK on %s", ifsp->if_name);
266 		return (0);
267 	}
268 
269 	cancel_ifs_timers(ifsp);
270 
271 	/*
272 	 * type has already been verified as ACK.  if type is not set,
273 	 * then we got a BOOTP packet.  we now fetch the t1, t2, and
274 	 * lease options out of the packet into variables.  they are
275 	 * returned as relative host-byte-ordered times.
276 	 */
277 
278 	get_pkt_times(ifsp->if_ack, &lease, &t1, &t2);
279 
280 	ifsp->if_t1	= t1;
281 	ifsp->if_t2	= t2;
282 	ifsp->if_lease	= lease;
283 
284 	if (ifsp->if_lease == DHCP_PERM) {
285 		dhcpmsg(MSG_INFO, "%s acquired permanent lease", ifsp->if_name);
286 		return (1);
287 	}
288 
289 	dhcpmsg(MSG_INFO, "%s acquired lease, expires %s", ifsp->if_name,
290 	    monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_lease));
291 
292 	dhcpmsg(MSG_INFO, "%s begins renewal at %s", ifsp->if_name,
293 	    monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t1));
294 
295 	dhcpmsg(MSG_INFO, "%s begins rebinding at %s", ifsp->if_name,
296 	    monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t2));
297 
298 	/*
299 	 * according to RFC2131, there is no minimum lease time, but don't
300 	 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN.
301 	 */
302 
303 	if (schedule_ifs_timer(ifsp, DHCP_LEASE_TIMER, lease, dhcp_expire) == 0)
304 		goto failure;
305 
306 	if (lease < DHCP_REBIND_MIN) {
307 		dhcpmsg(MSG_WARNING, "dhcp_bound: lease on %s is for "
308 		    "less than %d seconds!", ifsp->if_name, DHCP_REBIND_MIN);
309 		return (1);
310 	}
311 
312 	if (schedule_ifs_timer(ifsp, DHCP_T1_TIMER, t1, dhcp_renew) == 0)
313 		goto failure;
314 
315 	if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, t2, dhcp_rebind) == 0)
316 		goto failure;
317 
318 	return (1);
319 
320 failure:
321 	cancel_ifs_timers(ifsp);
322 	dhcpmsg(MSG_WARNING, "dhcp_bound: cannot schedule lease timers");
323 	return (0);
324 }
325 
326 /*
327  * configure_if(): configures an interface with DHCP parameters from an ACK
328  *
329  *   input: struct ifslist *: the interface to configure (with a valid if_ack)
330  *  output: int: 1 on success, 0 on failure
331  */
332 
333 static int
334 configure_if(struct ifslist *ifsp)
335 {
336 	struct ifreq		ifr;
337 	struct sockaddr_in	*sin;
338 	PKT_LIST		*ack = ifsp->if_ack;
339 
340 	/*
341 	 * if we're using DHCP, then we'll have a valid CD_SERVER_ID
342 	 * (we checked in dhcp_acknak()); set it now so that
343 	 * ifsp->if_server is valid in case we need to send_decline().
344 	 * note that we use comparisons against opts[CD_DHCP_TYPE]
345 	 * since we haven't set DHCP_IF_BOOTP yet (we don't do that
346 	 * until we're sure we want the offered address.)
347 	 */
348 
349 	if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL)
350 		(void) memcpy(&ifsp->if_server.s_addr,
351 		    ack->opts[CD_SERVER_ID]->value, sizeof (ipaddr_t));
352 
353 	ifsp->if_addr.s_addr = ack->pkt->yiaddr.s_addr;
354 	if (ifsp->if_addr.s_addr == htonl(INADDR_ANY)) {
355 		dhcpmsg(MSG_ERROR, "configure_if: got invalid IP address");
356 		return (0);
357 	}
358 
359 	(void) memset(&ifr, 0, sizeof (struct ifreq));
360 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
361 
362 	/*
363 	 * bring the interface online.  note that there is no optimal
364 	 * order here: it is considered bad taste (and in > solaris 7,
365 	 * likely illegal) to bring an interface up before it has an
366 	 * ip address.  however, due to an apparent bug in sun fddi
367 	 * 5.0, fddi will not obtain a network routing entry unless
368 	 * the interface is brought up before it has an ip address.
369 	 * we take the lesser of the two evils; if fddi customers have
370 	 * problems, they can get a newer fddi distribution which
371 	 * fixes the problem.
372 	 */
373 
374 	/* LINTED [ifr_addr is a sockaddr which will be aligned] */
375 	sin = (struct sockaddr_in *)&ifr.ifr_addr;
376 	sin->sin_family = AF_INET;
377 
378 	if (ack->opts[CD_SUBNETMASK] != NULL &&
379 	    ack->opts[CD_SUBNETMASK]->len == sizeof (ipaddr_t)) {
380 
381 		(void) memcpy(&ifsp->if_netmask.s_addr,
382 		    ack->opts[CD_SUBNETMASK]->value, sizeof (ipaddr_t));
383 
384 	} else {
385 
386 		if (ack->opts[CD_SUBNETMASK] != NULL &&
387 		    ack->opts[CD_SUBNETMASK]->len != sizeof (ipaddr_t))
388 			dhcpmsg(MSG_WARNING, "configure_if: specified subnet "
389 			    "mask length is %d instead of %d, ignoring",
390 			    ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t));
391 
392 		/*
393 		 * no legitimate IP subnet mask specified..  use best
394 		 * guess.  recall that if_addr is in network order, so
395 		 * imagine it's 0x11223344: then when it is read into
396 		 * a register on x86, it becomes 0x44332211, so we
397 		 * must ntohl() it to convert it to 0x11223344 in
398 		 * order to use the macros in <netinet/in.h>.
399 		 */
400 
401 		if (IN_CLASSA(ntohl(ifsp->if_addr.s_addr)))
402 			ifsp->if_netmask.s_addr = htonl(IN_CLASSA_NET);
403 		else if (IN_CLASSB(ntohl(ifsp->if_addr.s_addr)))
404 			ifsp->if_netmask.s_addr = htonl(IN_CLASSB_NET);
405 		else if (IN_CLASSC(ntohl(ifsp->if_addr.s_addr)))
406 			ifsp->if_netmask.s_addr = htonl(IN_CLASSC_NET);
407 		else	/* must be class d */
408 			ifsp->if_netmask.s_addr = htonl(IN_CLASSD_NET);
409 
410 		dhcpmsg(MSG_WARNING, "configure_if: no IP netmask specified "
411 		    "for %s, making best guess", ifsp->if_name);
412 	}
413 
414 	dhcpmsg(MSG_INFO, "setting IP netmask to %s on %s",
415 	    inet_ntoa(ifsp->if_netmask), ifsp->if_name);
416 
417 	sin->sin_addr = ifsp->if_netmask;
418 	if (ioctl(ifsp->if_sock_fd, SIOCSIFNETMASK, &ifr) == -1) {
419 		dhcpmsg(MSG_ERR, "cannot set IP netmask on %s", ifsp->if_name);
420 		return (0);
421 	}
422 
423 	dhcpmsg(MSG_INFO, "setting IP address to %s on %s",
424 	    inet_ntoa(ifsp->if_addr), ifsp->if_name);
425 
426 	sin->sin_addr = ifsp->if_addr;
427 	if (ioctl(ifsp->if_sock_fd, SIOCSIFADDR, &ifr) == -1) {
428 		dhcpmsg(MSG_ERR, "configure_if: cannot set IP address on %s",
429 		    ifsp->if_name);
430 		return (0);
431 	}
432 
433 	if (ack->opts[CD_BROADCASTADDR] != NULL &&
434 	    ack->opts[CD_BROADCASTADDR]->len == sizeof (ipaddr_t)) {
435 
436 		(void) memcpy(&ifsp->if_broadcast.s_addr,
437 		    ack->opts[CD_BROADCASTADDR]->value, sizeof (ipaddr_t));
438 
439 	} else {
440 
441 		if (ack->opts[CD_BROADCASTADDR] != NULL &&
442 		    ack->opts[CD_BROADCASTADDR]->len != sizeof (ipaddr_t))
443 			dhcpmsg(MSG_WARNING, "configure_if: specified "
444 			    "broadcast address length is %d instead of %d, "
445 			    "ignoring", ack->opts[CD_BROADCASTADDR]->len,
446 			    sizeof (ipaddr_t));
447 
448 		/*
449 		 * no legitimate IP broadcast specified.  compute it
450 		 * from the IP address and netmask.
451 		 */
452 
453 		ifsp->if_broadcast.s_addr = ifsp->if_addr.s_addr &
454 			ifsp->if_netmask.s_addr | ~ifsp->if_netmask.s_addr;
455 
456 		dhcpmsg(MSG_WARNING, "configure_if: no IP broadcast specified "
457 		    "for %s, making best guess", ifsp->if_name);
458 	}
459 
460 	if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
461 		dhcpmsg(MSG_ERR, "configure_if: cannot get interface flags for "
462 		    "%s", ifsp->if_name);
463 		return (0);
464 	}
465 
466 	ifr.ifr_flags |= IFF_UP;
467 
468 	if (ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
469 		dhcpmsg(MSG_ERR, "configure_if: cannot set interface flags for "
470 		    "%s", ifsp->if_name);
471 		return (0);
472 	}
473 
474 	/*
475 	 * the kernel will set the broadcast address for us as part of
476 	 * bringing the interface up.  since experience has shown that dhcp
477 	 * servers sometimes provide a bogus broadcast address, we let the
478 	 * kernel set it so that it's guaranteed to be correct.
479 	 *
480 	 * also, note any inconsistencies and save the broadcast address the
481 	 * kernel set so that we can watch for changes to it.
482 	 */
483 
484 	if (ioctl(ifsp->if_sock_fd, SIOCGIFBRDADDR, &ifr) == -1) {
485 		dhcpmsg(MSG_ERR, "configure_if: cannot get broadcast address "
486 		    "for %s", ifsp->if_name);
487 		return (0);
488 	}
489 
490 	if (ifsp->if_broadcast.s_addr != sin->sin_addr.s_addr) {
491 		dhcpmsg(MSG_WARNING, "incorrect broadcast address %s specified "
492 		    "for %s; ignoring", inet_ntoa(ifsp->if_broadcast),
493 		    ifsp->if_name);
494 	}
495 
496 	ifsp->if_broadcast = sin->sin_addr;
497 	dhcpmsg(MSG_INFO, "using broadcast address %s on %s",
498 	    inet_ntoa(ifsp->if_broadcast), ifsp->if_name);
499 	return (1);
500 }
501 
502 /*
503  * configure_bound(): configures routing with DHCP parameters from an ACK,
504  *		      and sets up the if_sock_ip_fd socket used for lease
505  *		      renewal.
506  *
507  *   input: struct ifslist *: the interface to configure (with a valid if_ack)
508  *  output: int: 1 on success, 0 on failure
509  */
510 
511 static int
512 configure_bound(struct ifslist *ifsp)
513 {
514 	PKT_LIST		*ack = ifsp->if_ack;
515 	DHCP_OPT		*router_list;
516 	int			i;
517 
518 	/*
519 	 * add each provided router; we'll clean them up when the
520 	 * interface goes away or when our lease expires.
521 	 */
522 
523 	router_list = ack->opts[CD_ROUTER];
524 	if (router_list && (router_list->len % sizeof (ipaddr_t)) == 0) {
525 
526 		ifsp->if_nrouters = router_list->len / sizeof (ipaddr_t);
527 		ifsp->if_routers  = malloc(router_list->len);
528 		if (ifsp->if_routers == NULL) {
529 			dhcpmsg(MSG_ERR, "configure_bound: cannot allocate "
530 			    "default router list, ignoring default routers");
531 			ifsp->if_nrouters = 0;
532 		}
533 
534 		for (i = 0; i < ifsp->if_nrouters; i++) {
535 
536 			(void) memcpy(&ifsp->if_routers[i].s_addr,
537 			    router_list->value + (i * sizeof (ipaddr_t)),
538 			    sizeof (ipaddr_t));
539 
540 			if (add_default_route(ifsp->if_name,
541 			    &ifsp->if_routers[i]) == 0) {
542 				dhcpmsg(MSG_ERR, "configure_bound: cannot add "
543 				    "default router %s on %s", inet_ntoa(
544 				    ifsp->if_routers[i]), ifsp->if_name);
545 				ifsp->if_routers[i].s_addr = htonl(INADDR_ANY);
546 				continue;
547 			}
548 
549 			dhcpmsg(MSG_INFO, "added default router %s on %s",
550 			    inet_ntoa(ifsp->if_routers[i]), ifsp->if_name);
551 		}
552 	}
553 
554 	ifsp->if_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
555 	if (ifsp->if_sock_ip_fd == -1) {
556 		dhcpmsg(MSG_ERR, "configure_bound: cannot create socket on %s",
557 		    ifsp->if_name);
558 		return (0);
559 	}
560 
561 	if (bind_sock(ifsp->if_sock_ip_fd, IPPORT_BOOTPC,
562 	    ntohl(ifsp->if_addr.s_addr)) == 0) {
563 		dhcpmsg(MSG_ERR, "configure_bound: cannot bind socket on %s",
564 		    ifsp->if_name);
565 		return (0);
566 	}
567 
568 	/*
569 	 * we wait until here to bind if_sock_fd because it turns out
570 	 * the kernel has difficulties doing binds before interfaces
571 	 * are up (although it may work sometimes, it doesn't work all
572 	 * the time.)  that's okay, because we don't use if_sock_fd
573 	 * for receiving data until we're BOUND anyway.
574 	 */
575 
576 	if (bind_sock(ifsp->if_sock_fd, IPPORT_BOOTPC, INADDR_BROADCAST) == 0) {
577 		dhcpmsg(MSG_ERR, "configure_bound: cannot bind broadcast "
578 		    "socket on %s", ifsp->if_name);
579 		return (0);
580 	}
581 
582 	/*
583 	 * we'll be using if_sock_fd for the remainder of the lease;
584 	 * blackhole if_dlpi_fd.
585 	 */
586 
587 	set_packet_filter(ifsp->if_dlpi_fd, blackhole_filter, 0, "blackhole");
588 
589 	if (ack->opts[CD_DHCP_TYPE] == NULL)
590 		ifsp->if_dflags	|= DHCP_IF_BOOTP;
591 
592 	dhcpmsg(MSG_DEBUG, "configure_bound: bound ifsp->if_sock_ip_fd");
593 	return (1);
594 }
595