xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c (revision 2187:0590c0271fda)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*2187Smeem  * Common Development and Distribution License (the "License").
6*2187Smeem  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*2187Smeem  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  * BOUND state of the DHCP client state machine.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <sys/socket.h>
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <string.h>
330Sstevel@tonic-gate #include <netinet/in.h>
340Sstevel@tonic-gate #include <sys/sockio.h>
350Sstevel@tonic-gate #include <unistd.h>
360Sstevel@tonic-gate #include <time.h>
370Sstevel@tonic-gate #include <arpa/inet.h>
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <sys/sysmacros.h>
400Sstevel@tonic-gate #include <dhcp_hostconf.h>
410Sstevel@tonic-gate #include <dhcpmsg.h>
420Sstevel@tonic-gate #include <stdio.h>			/* snprintf */
430Sstevel@tonic-gate 
440Sstevel@tonic-gate #include "defaults.h"
450Sstevel@tonic-gate #include "arp_check.h"
460Sstevel@tonic-gate #include "states.h"
470Sstevel@tonic-gate #include "packet.h"
480Sstevel@tonic-gate #include "util.h"
490Sstevel@tonic-gate #include "agent.h"
500Sstevel@tonic-gate #include "interface.h"
510Sstevel@tonic-gate #include "script_handler.h"
520Sstevel@tonic-gate 
530Sstevel@tonic-gate #define	IS_DHCP(plp)	((plp)->opts[CD_DHCP_TYPE] != NULL)
540Sstevel@tonic-gate 
550Sstevel@tonic-gate static int	configure_if(struct ifslist *);
560Sstevel@tonic-gate static int	configure_timers(struct ifslist *);
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*
590Sstevel@tonic-gate  * bound_event_cb(): callback for script_start on the event EVENT_BOUND
600Sstevel@tonic-gate  *
610Sstevel@tonic-gate  *   input: struct ifslist *: the interface configured
620Sstevel@tonic-gate  *	    const char *: unused
630Sstevel@tonic-gate  *  output: int: always 1
640Sstevel@tonic-gate  */
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /* ARGSUSED */
670Sstevel@tonic-gate static int
680Sstevel@tonic-gate bound_event_cb(struct ifslist *ifsp, const char *msg)
690Sstevel@tonic-gate {
700Sstevel@tonic-gate 	ipc_action_finish(ifsp, DHCP_IPC_SUCCESS);
710Sstevel@tonic-gate 	async_finish(ifsp);
720Sstevel@tonic-gate 	return (1);
730Sstevel@tonic-gate }
740Sstevel@tonic-gate 
750Sstevel@tonic-gate /*
760Sstevel@tonic-gate  * dhcp_bound(): configures an interface and ifs using information contained
770Sstevel@tonic-gate  *		 in the ACK packet and sets up lease timers.  before starting,
780Sstevel@tonic-gate  *		 the requested address is arped to make sure it's not in use.
790Sstevel@tonic-gate  *
800Sstevel@tonic-gate  *   input: struct ifslist *: the interface to move to bound
810Sstevel@tonic-gate  *	    PKT_LIST *: the ACK packet, or NULL if it should use ifsp->if_ack
820Sstevel@tonic-gate  *  output: int: 0 on failure, 1 on success
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate 
850Sstevel@tonic-gate int
860Sstevel@tonic-gate dhcp_bound(struct ifslist *ifsp, PKT_LIST *ack)
870Sstevel@tonic-gate {
880Sstevel@tonic-gate 	lease_t		cur_lease, new_lease;
890Sstevel@tonic-gate 	int		msg_level;
90*2187Smeem 	const char	*noext = "lease renewed but time not extended";
91*2187Smeem 	uint_t		minleft;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate 	if (ack != NULL) {
940Sstevel@tonic-gate 		/* If ack we're replacing is not the original, then free it */
950Sstevel@tonic-gate 		if (ifsp->if_ack != ifsp->if_orig_ack)
960Sstevel@tonic-gate 			free_pkt_list(&ifsp->if_ack);
970Sstevel@tonic-gate 		ifsp->if_ack = ack;
980Sstevel@tonic-gate 		/* Save the first ack as the original */
990Sstevel@tonic-gate 		if (ifsp->if_orig_ack == NULL)
1000Sstevel@tonic-gate 			ifsp->if_orig_ack = ack;
1010Sstevel@tonic-gate 	}
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	switch (ifsp->if_state) {
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	case ADOPTING:
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 		/*
1080Sstevel@tonic-gate 		 * if we're adopting an interface, the lease timers
1090Sstevel@tonic-gate 		 * only provide an upper bound since we don't know
1100Sstevel@tonic-gate 		 * from what time they are relative to.  assume we
1110Sstevel@tonic-gate 		 * have a lease time of at most DHCP_ADOPT_LEASE_MAX.
1120Sstevel@tonic-gate 		 */
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate 		if (!IS_DHCP(ifsp->if_ack))
1150Sstevel@tonic-gate 			return (0);
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 		(void) memcpy(&new_lease,
1180Sstevel@tonic-gate 		    ifsp->if_ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 		new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX));
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 		(void) memcpy(ifsp->if_ack->opts[CD_LEASE_TIME]->value,
1230Sstevel@tonic-gate 		    &new_lease, sizeof (lease_t));
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 		/*
1260Sstevel@tonic-gate 		 * we have no idea when the REQUEST that generated
1270Sstevel@tonic-gate 		 * this ACK was sent, but for diagnostic purposes
1280Sstevel@tonic-gate 		 * we'll assume its close to the current time.
1290Sstevel@tonic-gate 		 */
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 		ifsp->if_newstart_monosec = monosec();
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 		/* FALLTHRU into REQUESTING/INIT_REBOOT */
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	case REQUESTING:
1360Sstevel@tonic-gate 	case INIT_REBOOT:
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 		if (configure_if(ifsp) == 0)
1390Sstevel@tonic-gate 			return (0);
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 		if (configure_timers(ifsp) == 0)
1420Sstevel@tonic-gate 			return (0);
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 		/*
1450Sstevel@tonic-gate 		 * if the state is ADOPTING, event loop has not been started
146*2187Smeem 		 * at this time, so don't run the script.
1470Sstevel@tonic-gate 		 */
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 		if (ifsp->if_state != ADOPTING) {
1500Sstevel@tonic-gate 			(void) script_start(ifsp, EVENT_BOUND, bound_event_cb,
1510Sstevel@tonic-gate 				NULL, NULL);
1520Sstevel@tonic-gate 		}
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 		break;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	case RENEWING:
1570Sstevel@tonic-gate 	case REBINDING:
1580Sstevel@tonic-gate 	case BOUND:
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 		cur_lease = ifsp->if_lease;
1610Sstevel@tonic-gate 		if (configure_timers(ifsp) == 0)
1620Sstevel@tonic-gate 			return (0);
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 		/*
165*2187Smeem 		 * if the current lease is mysteriously close to the new
166*2187Smeem 		 * lease, warn the user.  unless there's less than a minute
167*2187Smeem 		 * left, round to the closest minute.
1680Sstevel@tonic-gate 		 */
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 		if (abs((ifsp->if_newstart_monosec + ifsp->if_lease) -
1710Sstevel@tonic-gate 		    (ifsp->if_curstart_monosec + cur_lease)) < DHCP_LEASE_EPS) {
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 			if (ifsp->if_lease < DHCP_LEASE_ERROR_THRESH)
1740Sstevel@tonic-gate 				msg_level = MSG_ERROR;
1750Sstevel@tonic-gate 			else
1760Sstevel@tonic-gate 				msg_level = MSG_VERBOSE;
1770Sstevel@tonic-gate 
178*2187Smeem 			minleft = (ifsp->if_lease + 30) / 60;
179*2187Smeem 
180*2187Smeem 			if (ifsp->if_lease < 60) {
181*2187Smeem 				dhcpmsg(msg_level, "%s; expires in %d seconds",
182*2187Smeem 				    noext, ifsp->if_lease);
183*2187Smeem 			} else if (minleft == 1) {
184*2187Smeem 				dhcpmsg(msg_level, "%s; expires in 1 minute",
185*2187Smeem 				    noext);
186*2187Smeem 			} else {
187*2187Smeem 				dhcpmsg(msg_level, "%s; expires in %d minutes",
188*2187Smeem 				    noext, minleft);
189*2187Smeem 			}
1900Sstevel@tonic-gate 		}
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 		(void) script_start(ifsp, EVENT_EXTEND, bound_event_cb,
1930Sstevel@tonic-gate 		    NULL, NULL);
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 		break;
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	case INFORM_SENT:
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 		(void) bound_event_cb(ifsp, NULL);
2000Sstevel@tonic-gate 		ifsp->if_state = INFORMATION;
2010Sstevel@tonic-gate 		break;
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	default:
2040Sstevel@tonic-gate 		/* something is really bizarre... */
2050Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "dhcp_bound: called in unexpected state");
2060Sstevel@tonic-gate 		return (0);
2070Sstevel@tonic-gate 	}
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	if (ifsp->if_state != INFORMATION) {
2100Sstevel@tonic-gate 		ifsp->if_state = BOUND;
2110Sstevel@tonic-gate 		ifsp->if_curstart_monosec = ifsp->if_newstart_monosec;
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	/*
2150Sstevel@tonic-gate 	 * remove any stale hostconf file that might be lying around for
2160Sstevel@tonic-gate 	 * this interface. (in general, it's harmless, since we'll write a
2170Sstevel@tonic-gate 	 * fresh one when we exit anyway, but just to reduce confusion..)
2180Sstevel@tonic-gate 	 */
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	(void) remove_hostconf(ifsp->if_name);
2210Sstevel@tonic-gate 	return (1);
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate /*
2250Sstevel@tonic-gate  * configure_timers(): configures the lease timers on an interface
2260Sstevel@tonic-gate  *
2270Sstevel@tonic-gate  *   input: struct ifslist *: the interface to configure (with a valid if_ack)
2280Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
2290Sstevel@tonic-gate  */
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate int
2320Sstevel@tonic-gate configure_timers(struct ifslist *ifsp)
2330Sstevel@tonic-gate {
2340Sstevel@tonic-gate 	lease_t		lease, t1, t2;
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 	if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL &&
2370Sstevel@tonic-gate 	    (ifsp->if_ack->opts[CD_LEASE_TIME] == NULL ||
2380Sstevel@tonic-gate 	    ifsp->if_ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
2390Sstevel@tonic-gate 		send_decline(ifsp, "Missing or corrupted lease time",
2400Sstevel@tonic-gate 		    &ifsp->if_ack->pkt->yiaddr);
2410Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "configure_timers: missing or corrupted "
2420Sstevel@tonic-gate 		    "lease time in ACK on %s", ifsp->if_name);
2430Sstevel@tonic-gate 		return (0);
2440Sstevel@tonic-gate 	}
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	cancel_ifs_timers(ifsp);
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 	/*
2490Sstevel@tonic-gate 	 * type has already been verified as ACK.  if type is not set,
2500Sstevel@tonic-gate 	 * then we got a BOOTP packet.  we now fetch the t1, t2, and
2510Sstevel@tonic-gate 	 * lease options out of the packet into variables.  they are
2520Sstevel@tonic-gate 	 * returned as relative host-byte-ordered times.
2530Sstevel@tonic-gate 	 */
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	get_pkt_times(ifsp->if_ack, &lease, &t1, &t2);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	ifsp->if_t1	= t1;
2580Sstevel@tonic-gate 	ifsp->if_t2	= t2;
2590Sstevel@tonic-gate 	ifsp->if_lease	= lease;
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 	if (ifsp->if_lease == DHCP_PERM) {
2620Sstevel@tonic-gate 		dhcpmsg(MSG_INFO, "%s acquired permanent lease", ifsp->if_name);
2630Sstevel@tonic-gate 		return (1);
2640Sstevel@tonic-gate 	}
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "%s acquired lease, expires %s", ifsp->if_name,
2670Sstevel@tonic-gate 	    monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_lease));
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "%s begins renewal at %s", ifsp->if_name,
2700Sstevel@tonic-gate 	    monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t1));
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "%s begins rebinding at %s", ifsp->if_name,
2730Sstevel@tonic-gate 	    monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t2));
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	/*
2760Sstevel@tonic-gate 	 * according to RFC2131, there is no minimum lease time, but don't
2770Sstevel@tonic-gate 	 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN.
2780Sstevel@tonic-gate 	 */
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	if (schedule_ifs_timer(ifsp, DHCP_LEASE_TIMER, lease, dhcp_expire) == 0)
2810Sstevel@tonic-gate 		goto failure;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	if (lease < DHCP_REBIND_MIN) {
2840Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "dhcp_bound: lease on %s is for "
2850Sstevel@tonic-gate 		    "less than %d seconds!", ifsp->if_name, DHCP_REBIND_MIN);
2860Sstevel@tonic-gate 		return (1);
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	if (schedule_ifs_timer(ifsp, DHCP_T1_TIMER, t1, dhcp_renew) == 0)
2900Sstevel@tonic-gate 		goto failure;
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, t2, dhcp_rebind) == 0)
2930Sstevel@tonic-gate 		goto failure;
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	return (1);
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate failure:
2980Sstevel@tonic-gate 	cancel_ifs_timers(ifsp);
2990Sstevel@tonic-gate 	dhcpmsg(MSG_WARNING, "dhcp_bound: cannot schedule lease timers");
3000Sstevel@tonic-gate 	return (0);
3010Sstevel@tonic-gate }
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate /*
3040Sstevel@tonic-gate  * configure_if(): configures an interface with DHCP parameters from an ACK
3050Sstevel@tonic-gate  *
3060Sstevel@tonic-gate  *   input: struct ifslist *: the interface to configure (with a valid if_ack)
3070Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
3080Sstevel@tonic-gate  */
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate static int
3110Sstevel@tonic-gate configure_if(struct ifslist *ifsp)
3120Sstevel@tonic-gate {
3130Sstevel@tonic-gate 	struct ifreq		ifr;
3140Sstevel@tonic-gate 	struct sockaddr_in	*sin;
3150Sstevel@tonic-gate 	PKT_LIST		*ack = ifsp->if_ack;
3160Sstevel@tonic-gate 	DHCP_OPT		*router_list;
3170Sstevel@tonic-gate 	uchar_t			*target_hwaddr;
3180Sstevel@tonic-gate 	int			i;
3190Sstevel@tonic-gate 	char			in_use[256] = "IP address already in use by";
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	/*
3220Sstevel@tonic-gate 	 * if we're using DHCP, then we'll have a valid CD_SERVER_ID
3230Sstevel@tonic-gate 	 * (we checked in dhcp_acknak()); set it now so that
3240Sstevel@tonic-gate 	 * ifsp->if_server is valid in case we need to send_decline().
3250Sstevel@tonic-gate 	 * note that we use comparisons against opts[CD_DHCP_TYPE]
3260Sstevel@tonic-gate 	 * since we haven't set DHCP_IF_BOOTP yet (we don't do that
3270Sstevel@tonic-gate 	 * until we're sure we want the offered address.)
3280Sstevel@tonic-gate 	 */
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL)
3310Sstevel@tonic-gate 		(void) memcpy(&ifsp->if_server.s_addr,
3320Sstevel@tonic-gate 		    ack->opts[CD_SERVER_ID]->value, sizeof (ipaddr_t));
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	/* no big deal if this fails; we'll just have less diagnostics */
3350Sstevel@tonic-gate 	target_hwaddr = malloc(ifsp->if_hwlen);
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	if (arp_check(ifsp, 0, ack->pkt->yiaddr.s_addr, target_hwaddr,
3380Sstevel@tonic-gate 	    ifsp->if_hwlen, df_get_int(ifsp->if_name, DF_ARP_WAIT)) == 1) {
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 		for (i = 0; i < ifsp->if_hwlen; i++)
3410Sstevel@tonic-gate 			(void) snprintf(in_use, sizeof (in_use), "%s %02x",
3420Sstevel@tonic-gate 			    in_use, target_hwaddr[i]);
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, in_use);
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 		if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL)
3470Sstevel@tonic-gate 			send_decline(ifsp, in_use, &ack->pkt->yiaddr);
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 		ifsp->if_bad_offers++;
3500Sstevel@tonic-gate 		free(target_hwaddr);
3510Sstevel@tonic-gate 		return (0);
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate 	free(target_hwaddr);
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	ifsp->if_addr.s_addr = ack->pkt->yiaddr.s_addr;
3560Sstevel@tonic-gate 	if (ifsp->if_addr.s_addr == htonl(INADDR_ANY)) {
3570Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "configure_if: got invalid IP address");
3580Sstevel@tonic-gate 		return (0);
3590Sstevel@tonic-gate 	}
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 	(void) memset(&ifr, 0, sizeof (struct ifreq));
3620Sstevel@tonic-gate 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	/*
3650Sstevel@tonic-gate 	 * bring the interface online.  note that there is no optimal
3660Sstevel@tonic-gate 	 * order here: it is considered bad taste (and in > solaris 7,
3670Sstevel@tonic-gate 	 * likely illegal) to bring an interface up before it has an
3680Sstevel@tonic-gate 	 * ip address.  however, due to an apparent bug in sun fddi
3690Sstevel@tonic-gate 	 * 5.0, fddi will not obtain a network routing entry unless
3700Sstevel@tonic-gate 	 * the interface is brought up before it has an ip address.
3710Sstevel@tonic-gate 	 * we take the lesser of the two evils; if fddi customers have
3720Sstevel@tonic-gate 	 * problems, they can get a newer fddi distribution which
3730Sstevel@tonic-gate 	 * fixes the problem.
3740Sstevel@tonic-gate 	 */
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	/* LINTED [ifr_addr is a sockaddr which will be aligned] */
3770Sstevel@tonic-gate 	sin = (struct sockaddr_in *)&ifr.ifr_addr;
3780Sstevel@tonic-gate 	sin->sin_family = AF_INET;
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	if (ack->opts[CD_SUBNETMASK] != NULL &&
3810Sstevel@tonic-gate 	    ack->opts[CD_SUBNETMASK]->len == sizeof (ipaddr_t)) {
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 		(void) memcpy(&ifsp->if_netmask.s_addr,
3840Sstevel@tonic-gate 		    ack->opts[CD_SUBNETMASK]->value, sizeof (ipaddr_t));
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 	} else {
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 		if (ack->opts[CD_SUBNETMASK] != NULL &&
3890Sstevel@tonic-gate 		    ack->opts[CD_SUBNETMASK]->len != sizeof (ipaddr_t))
3900Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "configure_if: specified subnet "
3910Sstevel@tonic-gate 			    "mask length is %d instead of %d, ignoring",
3920Sstevel@tonic-gate 			    ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t));
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 		/*
3950Sstevel@tonic-gate 		 * no legitimate IP subnet mask specified..  use best
3960Sstevel@tonic-gate 		 * guess.  recall that if_addr is in network order, so
3970Sstevel@tonic-gate 		 * imagine it's 0x11223344: then when it is read into
3980Sstevel@tonic-gate 		 * a register on x86, it becomes 0x44332211, so we
3990Sstevel@tonic-gate 		 * must ntohl() it to convert it to 0x11223344 in
4000Sstevel@tonic-gate 		 * order to use the macros in <netinet/in.h>.
4010Sstevel@tonic-gate 		 */
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 		if (IN_CLASSA(ntohl(ifsp->if_addr.s_addr)))
4040Sstevel@tonic-gate 			ifsp->if_netmask.s_addr = htonl(IN_CLASSA_NET);
4050Sstevel@tonic-gate 		else if (IN_CLASSB(ntohl(ifsp->if_addr.s_addr)))
4060Sstevel@tonic-gate 			ifsp->if_netmask.s_addr = htonl(IN_CLASSB_NET);
4070Sstevel@tonic-gate 		else if (IN_CLASSC(ntohl(ifsp->if_addr.s_addr)))
4080Sstevel@tonic-gate 			ifsp->if_netmask.s_addr = htonl(IN_CLASSC_NET);
4090Sstevel@tonic-gate 		else	/* must be class d */
4100Sstevel@tonic-gate 			ifsp->if_netmask.s_addr = htonl(IN_CLASSD_NET);
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "configure_if: no IP netmask specified "
4130Sstevel@tonic-gate 		    "for %s, making best guess", ifsp->if_name);
4140Sstevel@tonic-gate 	}
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "setting IP netmask to %s on %s",
4170Sstevel@tonic-gate 	    inet_ntoa(ifsp->if_netmask), ifsp->if_name);
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	sin->sin_addr = ifsp->if_netmask;
4200Sstevel@tonic-gate 	if (ioctl(ifsp->if_sock_fd, SIOCSIFNETMASK, &ifr) == -1) {
4210Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "cannot set IP netmask on %s", ifsp->if_name);
4220Sstevel@tonic-gate 		return (0);
4230Sstevel@tonic-gate 	}
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "setting IP address to %s on %s",
4260Sstevel@tonic-gate 	    inet_ntoa(ifsp->if_addr), ifsp->if_name);
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	sin->sin_addr = ifsp->if_addr;
4290Sstevel@tonic-gate 	if (ioctl(ifsp->if_sock_fd, SIOCSIFADDR, &ifr) == -1) {
4300Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "configure_if: cannot set IP address on %s",
4310Sstevel@tonic-gate 		    ifsp->if_name);
4320Sstevel@tonic-gate 		return (0);
4330Sstevel@tonic-gate 	}
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 	if (ack->opts[CD_BROADCASTADDR] != NULL &&
4360Sstevel@tonic-gate 	    ack->opts[CD_BROADCASTADDR]->len == sizeof (ipaddr_t)) {
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 		(void) memcpy(&ifsp->if_broadcast.s_addr,
4390Sstevel@tonic-gate 		    ack->opts[CD_BROADCASTADDR]->value, sizeof (ipaddr_t));
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 	} else {
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 		if (ack->opts[CD_BROADCASTADDR] != NULL &&
4440Sstevel@tonic-gate 		    ack->opts[CD_BROADCASTADDR]->len != sizeof (ipaddr_t))
4450Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "configure_if: specified "
4460Sstevel@tonic-gate 			    "broadcast address length is %d instead of %d, "
4470Sstevel@tonic-gate 			    "ignoring", ack->opts[CD_BROADCASTADDR]->len,
4480Sstevel@tonic-gate 			    sizeof (ipaddr_t));
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 		/*
4510Sstevel@tonic-gate 		 * no legitimate IP broadcast specified.  compute it
4520Sstevel@tonic-gate 		 * from the IP address and netmask.
4530Sstevel@tonic-gate 		 */
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 		ifsp->if_broadcast.s_addr = ifsp->if_addr.s_addr &
4560Sstevel@tonic-gate 			ifsp->if_netmask.s_addr | ~ifsp->if_netmask.s_addr;
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "configure_if: no IP broadcast specified "
4590Sstevel@tonic-gate 		    "for %s, making best guess", ifsp->if_name);
4600Sstevel@tonic-gate 	}
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 	if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
4630Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "configure_if: cannot get interface flags for "
4640Sstevel@tonic-gate 		    "%s", ifsp->if_name);
4650Sstevel@tonic-gate 		return (0);
4660Sstevel@tonic-gate 	}
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	ifr.ifr_flags |= IFF_UP;
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	if (ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
4710Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "configure_if: cannot set interface flags for "
4720Sstevel@tonic-gate 		    "%s", ifsp->if_name);
4730Sstevel@tonic-gate 		return (0);
4740Sstevel@tonic-gate 	}
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 	/*
4770Sstevel@tonic-gate 	 * the kernel will set the broadcast address for us as part of
4780Sstevel@tonic-gate 	 * bringing the interface up.  since experience has shown that dhcp
4790Sstevel@tonic-gate 	 * servers sometimes provide a bogus broadcast address, we let the
4800Sstevel@tonic-gate 	 * kernel set it so that it's guaranteed to be correct.
4810Sstevel@tonic-gate 	 *
4820Sstevel@tonic-gate 	 * also, note any inconsistencies and save the broadcast address the
4830Sstevel@tonic-gate 	 * kernel set so that we can watch for changes to it.
4840Sstevel@tonic-gate 	 */
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	if (ioctl(ifsp->if_sock_fd, SIOCGIFBRDADDR, &ifr) == -1) {
4870Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "configure_if: cannot get broadcast address "
4880Sstevel@tonic-gate 		    "for %s", ifsp->if_name);
4890Sstevel@tonic-gate 		return (0);
4900Sstevel@tonic-gate 	}
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	if (ifsp->if_broadcast.s_addr != sin->sin_addr.s_addr) {
4930Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "incorrect broadcast address %s specified "
4940Sstevel@tonic-gate 		    "for %s; ignoring", inet_ntoa(ifsp->if_broadcast),
4950Sstevel@tonic-gate 		    ifsp->if_name);
4960Sstevel@tonic-gate 	}
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 	ifsp->if_broadcast = sin->sin_addr;
4990Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "using broadcast address %s on %s",
5000Sstevel@tonic-gate 	    inet_ntoa(ifsp->if_broadcast), ifsp->if_name);
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	/*
5030Sstevel@tonic-gate 	 * add each provided router; we'll clean them up when the
5040Sstevel@tonic-gate 	 * interface goes away or when our lease expires.
5050Sstevel@tonic-gate 	 */
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	router_list = ack->opts[CD_ROUTER];
5080Sstevel@tonic-gate 	if (router_list && (router_list->len % sizeof (ipaddr_t)) == 0) {
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 		ifsp->if_nrouters = router_list->len / sizeof (ipaddr_t);
5110Sstevel@tonic-gate 		ifsp->if_routers  = malloc(router_list->len);
5120Sstevel@tonic-gate 		if (ifsp->if_routers == NULL) {
5130Sstevel@tonic-gate 			dhcpmsg(MSG_ERR, "configure_if: cannot allocate "
5140Sstevel@tonic-gate 			    "default router list, ignoring default routers");
5150Sstevel@tonic-gate 			ifsp->if_nrouters = 0;
5160Sstevel@tonic-gate 		}
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 		for (i = 0; i < ifsp->if_nrouters; i++) {
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 			(void) memcpy(&ifsp->if_routers[i].s_addr,
5210Sstevel@tonic-gate 			    router_list->value + (i * sizeof (ipaddr_t)),
5220Sstevel@tonic-gate 			    sizeof (ipaddr_t));
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 			if (add_default_route(ifsp->if_name,
5250Sstevel@tonic-gate 			    &ifsp->if_routers[i]) == 0) {
5260Sstevel@tonic-gate 				dhcpmsg(MSG_ERR, "configure_if: cannot add "
5270Sstevel@tonic-gate 				    "default router %s on %s", inet_ntoa(
5280Sstevel@tonic-gate 				    ifsp->if_routers[i]), ifsp->if_name);
5290Sstevel@tonic-gate 				ifsp->if_routers[i].s_addr = htonl(INADDR_ANY);
5300Sstevel@tonic-gate 				continue;
5310Sstevel@tonic-gate 			}
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 			dhcpmsg(MSG_INFO, "added default router %s on %s",
5340Sstevel@tonic-gate 			    inet_ntoa(ifsp->if_routers[i]), ifsp->if_name);
5350Sstevel@tonic-gate 		}
5360Sstevel@tonic-gate 	}
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	ifsp->if_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
5390Sstevel@tonic-gate 	if (ifsp->if_sock_ip_fd == -1) {
5400Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "configure_if: cannot create socket on %s",
5410Sstevel@tonic-gate 		    ifsp->if_name);
5420Sstevel@tonic-gate 		return (0);
5430Sstevel@tonic-gate 	}
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	if (bind_sock(ifsp->if_sock_ip_fd, IPPORT_BOOTPC,
5460Sstevel@tonic-gate 	    ntohl(ifsp->if_addr.s_addr)) == 0) {
5470Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "configure_if: cannot bind socket on %s",
5480Sstevel@tonic-gate 		    ifsp->if_name);
5490Sstevel@tonic-gate 		return (0);
5500Sstevel@tonic-gate 	}
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	/*
5530Sstevel@tonic-gate 	 * we wait until here to bind if_sock_fd because it turns out
5540Sstevel@tonic-gate 	 * the kernel has difficulties doing binds before interfaces
5550Sstevel@tonic-gate 	 * are up (although it may work sometimes, it doesn't work all
5560Sstevel@tonic-gate 	 * the time.)  that's okay, because we don't use if_sock_fd
5570Sstevel@tonic-gate 	 * for receiving data until we're BOUND anyway.
5580Sstevel@tonic-gate 	 */
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	if (bind_sock(ifsp->if_sock_fd, IPPORT_BOOTPC, INADDR_BROADCAST) == 0) {
5610Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "configure_if: cannot bind broadcast socket "
5620Sstevel@tonic-gate 		    "on %s", ifsp->if_name);
5630Sstevel@tonic-gate 		return (0);
5640Sstevel@tonic-gate 	}
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	/*
5670Sstevel@tonic-gate 	 * we'll be using if_sock_fd for the remainder of the lease;
5680Sstevel@tonic-gate 	 * blackhole if_dlpi_fd.
5690Sstevel@tonic-gate 	 */
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	set_packet_filter(ifsp->if_dlpi_fd, blackhole_filter, 0, "blackhole");
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	if (ack->opts[CD_DHCP_TYPE] == NULL)
5740Sstevel@tonic-gate 		ifsp->if_dflags	|= DHCP_IF_BOOTP;
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "configure_if: bound ifsp->if_sock_ip_fd");
5770Sstevel@tonic-gate 	return (1);
5780Sstevel@tonic-gate }
579