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