xref: /illumos-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/adopt.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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  * ADOPTING state of the client state machine.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/types.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <sys/sockio.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <sys/systeminfo.h>
39 #include <netinet/inetutil.h>
40 #include <netinet/dhcp.h>
41 #include <dhcpmsg.h>
42 
43 #include "async.h"
44 #include "util.h"
45 #include "packet.h"
46 #include "interface.h"
47 #include "states.h"
48 
49 
50 typedef struct {
51 	char		dk_if_name[IFNAMSIZ];
52 	char		dk_ack[1];
53 } dhcp_kcache_t;
54 
55 static int	get_dhcp_kcache(dhcp_kcache_t **, size_t *);
56 
57 /*
58  * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot
59  *
60  *   input: void
61  *  output: int: nonzero on success, zero on failure
62  */
63 
64 int
65 dhcp_adopt(void)
66 {
67 	int		retval;
68 	dhcp_kcache_t	*kcache = NULL;
69 	size_t		kcache_size;
70 	PKT_LIST	*plp = NULL;
71 	struct ifslist	*ifsp;
72 
73 	retval = get_dhcp_kcache(&kcache, &kcache_size);
74 	if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) {
75 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache");
76 		goto failure;
77 	}
78 
79 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name);
80 
81 	/*
82 	 * convert the kernel's ACK into binary
83 	 */
84 
85 	plp = calloc(1, sizeof (PKT_LIST));
86 	if (plp == NULL)
87 		goto failure;
88 
89 	plp->len = strlen(kcache->dk_ack) / 2;
90 	plp->pkt = malloc(plp->len);
91 	if (plp->pkt == NULL)
92 		goto failure;
93 
94 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len);
95 
96 	if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, &plp->len)
97 	    != 0) {
98 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK");
99 		goto failure;
100 	}
101 
102 	if (dhcp_options_scan(plp, B_TRUE) != 0) {
103 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK");
104 		goto failure;
105 	}
106 
107 	/*
108 	 * make an interface to represent the "cached interface" in
109 	 * the kernel, hook up the ACK packet we made, and send out
110 	 * the extend request (to attempt to renew the lease).
111 	 *
112 	 * we do a send_extend() instead of doing a dhcp_init_reboot()
113 	 * because although dhcp_init_reboot() is more correct from a
114 	 * protocol perspective, it introduces a window where a
115 	 * diskless client has no IP address but may need to page in
116 	 * more of this program.  we could mlockall(), but that's
117 	 * going to be a mess, especially with handling malloc() and
118 	 * stack growth, so it's easier to just renew().  the only
119 	 * catch here is that if we are not granted a renewal, we're
120 	 * totally hosed and can only bail out.
121 	 */
122 
123 	ifsp = insert_ifs(kcache->dk_if_name, B_TRUE, &retval);
124 	if (ifsp == NULL)
125 		goto failure;
126 
127 	ifsp->if_state   = ADOPTING;
128 	ifsp->if_dflags |= DHCP_IF_PRIMARY;
129 
130 	/*
131 	 * move to BOUND and use the information in our ACK packet.
132 	 * adoption will continue after DAD via dhcp_adopt_complete.
133 	 */
134 
135 	if (dhcp_bound(ifsp, plp) == 0) {
136 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet");
137 		goto failure;
138 	}
139 
140 	free(kcache);
141 	return (1);
142 
143 failure:
144 	free(kcache);
145 	if (plp != NULL)
146 		free(plp->pkt);
147 	free(plp);
148 	return (0);
149 }
150 
151 /*
152  * dhcp_adopt_complete(): completes interface adoption process after kernel
153  *			  duplicate address detection (DAD) is done.
154  *
155  *   input: struct ifslist *: the interface on which a lease is being adopted
156  *  output: none
157  */
158 
159 void
160 dhcp_adopt_complete(struct ifslist *ifsp)
161 {
162 	dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption");
163 
164 	if (async_start(ifsp, DHCP_EXTEND, B_FALSE) == 0) {
165 		dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed");
166 		return;
167 	}
168 
169 	if (dhcp_extending(ifsp) == 0) {
170 		dhcpmsg(MSG_CRIT,
171 		    "dhcp_adopt_complete: cannot send renew request");
172 		return;
173 	}
174 
175 	if (grandparent != (pid_t)0) {
176 		dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%i)"
177 		    " to exit.", grandparent);
178 		(void) kill(grandparent, SIGALRM);
179 	}
180 }
181 
182 /*
183  * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel
184  *
185  *   input: dhcp_kcache_t **: a dynamically-allocated cache packet
186  *	    size_t *: the length of that packet (on return)
187  *  output: int: nonzero on success, zero on failure
188  */
189 
190 static int
191 get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size)
192 {
193 	char	dummy;
194 	long	size;
195 
196 	size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy));
197 	if (size == -1)
198 		return (0);
199 
200 	*kcache_size   = size;
201 	*kernel_cachep = malloc(*kcache_size);
202 	if (*kernel_cachep == NULL)
203 		return (0);
204 
205 	(void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size);
206 	return (1);
207 }
208