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