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