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 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <net/if.h>
35 #include <netinet/dhcp.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <time.h>
39 #include <string.h>			/* memcpy */
40 #include <fcntl.h>
41 #include <limits.h>
42 
43 #include "dhcp_hostconf.h"
44 
45 static void		relativize_time(DHCP_OPT *, time_t, time_t);
46 
47 /*
48  * ifname_to_hostconf(): converts an interface name into a hostconf file for
49  *			 that interface
50  *
51  *   input: const char *: the interface name
52  *  output: char *: the hostconf filename
53  *    note: uses an internal static buffer (not threadsafe)
54  */
55 
56 char *
57 ifname_to_hostconf(const char *ifname)
58 {
59 	static char filename[sizeof (DHCP_HOSTCONF_TMPL) + IFNAMSIZ];
60 
61 	(void) snprintf(filename, sizeof (filename), "%s%s%s",
62 	    DHCP_HOSTCONF_PREFIX, ifname, DHCP_HOSTCONF_SUFFIX);
63 
64 	return (filename);
65 }
66 
67 /*
68  * remove_hostconf(): removes an interface.dhc file
69  *
70  *   input: const char *: the interface name
71  *  output: int: 0 if the file is removed, -1 if it can't be removed
72  *          (errno is set)
73  */
74 
75 int
76 remove_hostconf(const char *ifname)
77 {
78 	return (unlink(ifname_to_hostconf(ifname)));
79 }
80 
81 /*
82  * read_hostconf(): reads the contents of an <if>.dhc file into a PKT_LIST
83  *
84  *   input: const char *: the interface name
85  *	    PKT_LIST **: a pointer to a PKT_LIST * to store the info in
86  *	    uint_t: the length of the list of PKT_LISTs
87  *  output: int: 0 if the file is read and loaded into the PKT_LIST *
88  *	    successfully, -1 otherwise (errno is set)
89  *    note: the PKT and PKT_LISTs are dynamically allocated here
90  */
91 
92 int
93 read_hostconf(const char *ifname, PKT_LIST **plpp, uint_t plplen)
94 {
95 	PKT_LIST	*plp = NULL;
96 	PKT		*pkt = NULL;
97 	int		fd;
98 	time_t		orig_time, current_time = time(NULL);
99 	uint32_t	lease;
100 	uint32_t	magic;
101 	int		pcnt = 0;
102 	int		retval;
103 
104 	fd = open(ifname_to_hostconf(ifname), O_RDONLY);
105 	if (fd == -1)
106 		return (-1);
107 
108 	if (read(fd, &magic, sizeof (magic)) != sizeof (magic))
109 		goto failure;
110 
111 	if (magic != DHCP_HOSTCONF_MAGIC)
112 		goto failure;
113 
114 	if (read(fd, &orig_time, sizeof (orig_time)) != sizeof (orig_time))
115 		goto failure;
116 
117 	/*
118 	 * read the packet back in from disk, and run it through
119 	 * dhcp_options_scan(). note that we use calloc() since
120 	 * dhcp_options_scan() relies on the packet being zeroed.
121 	 */
122 
123 	for (pcnt = 0; pcnt < plplen; pcnt++) {
124 
125 		plp = NULL;
126 		pkt = NULL;
127 
128 		if ((plp = calloc(1, sizeof (PKT_LIST))) == NULL)
129 			goto failure;
130 
131 		retval = read(fd, &plp->len, sizeof (plp->len));
132 		if (retval == 0 && pcnt != 0) {
133 			/*
134 			 * Reached end of file on a boundary, but after
135 			 * we've read at least one packet, so we consider
136 			 * this successful, allowing us to use files from
137 			 * older versions of the agent happily.
138 			 */
139 			free(plp);
140 			break;
141 		} else if (retval != sizeof (plp->len))
142 			goto failure;
143 
144 		if ((pkt = malloc(plp->len)) == NULL)
145 			goto failure;
146 
147 		if (read(fd, pkt, plp->len) != plp->len)
148 			goto failure;
149 
150 		plp->pkt = pkt;
151 
152 		plpp[pcnt] = plp;
153 
154 		if (dhcp_options_scan(plp, B_TRUE) != 0)
155 			goto failure;
156 
157 		/*
158 		 * First packet used to validate that we're interested,
159 		 * the rest are presumed to be historical reference and
160 		 * are not relativized
161 		 */
162 		if (pcnt == 0)
163 			continue;
164 
165 		/*
166 		 * make sure the lease is still valid.
167 		 */
168 
169 		if (plp->opts[CD_LEASE_TIME] != NULL &&
170 		    plp->opts[CD_LEASE_TIME]->len == sizeof (lease_t)) {
171 
172 			(void) memcpy(&lease, plp->opts[CD_LEASE_TIME]->value,
173 			    sizeof (lease_t));
174 
175 			lease = ntohl(lease);
176 			if ((lease != DHCP_PERM) &&
177 			    (orig_time + lease) <= current_time)
178 				goto failure;
179 		}
180 
181 		relativize_time(plp->opts[CD_T1_TIME], orig_time, current_time);
182 		relativize_time(plp->opts[CD_T2_TIME], orig_time, current_time);
183 		relativize_time(plp->opts[CD_LEASE_TIME], orig_time,
184 		    current_time);
185 	}
186 
187 	(void) close(fd);
188 	return (pcnt);
189 
190 failure:
191 	free(pkt);
192 	free(plp);
193 	while (pcnt-- > 0) {
194 		free(plpp[pcnt]->pkt);
195 		free(plpp[pcnt]);
196 	}
197 	(void) close(fd);
198 	return (-1);
199 }
200 
201 /*
202  * write_hostconf(): writes the contents of a PKT_LIST into an <if>.dhc file
203  *
204  *   input: const char *: the interface name
205  *	    PKT_LIST **: a list of pointers to PKT_LIST to write
206  *	    int: length of the list of PKT_LIST pointers
207  *	    time_t: a starting time to treat the relative lease times
208  *		    in the first packet as relative to
209  *  output: int: 0 if the file is written successfully, -1 otherwise
210  *	    (errno is set)
211  */
212 
213 int
214 write_hostconf(
215     const char *ifname,
216     PKT_LIST *pl[],
217     uint_t pllen,
218     time_t relative_to)
219 {
220 	int		fd;
221 	struct iovec	iov[IOV_MAX];
222 	int		retval;
223 	uint32_t	magic = DHCP_HOSTCONF_MAGIC;
224 	ssize_t		explen = 0; /* Expected length of write */
225 	int		i, iovlen = 0;
226 
227 	fd = open(ifname_to_hostconf(ifname), O_WRONLY|O_CREAT|O_TRUNC, 0600);
228 	if (fd == -1)
229 		return (-1);
230 
231 	/*
232 	 * first write our magic number, then the relative time of the
233 	 * leases, then for each packet we write the length of the packet
234 	 * followed by the packet.  we will then use the relative time in
235 	 * read_hostconf() to recalculate the lease times for the first packet.
236 	 */
237 
238 	iov[iovlen].iov_base = (caddr_t)&magic;
239 	explen += iov[iovlen++].iov_len  = sizeof (magic);
240 	iov[iovlen].iov_base = (caddr_t)&relative_to;
241 	explen += iov[iovlen++].iov_len  = sizeof (relative_to);
242 	for (i = 0; i < pllen && iovlen < (IOV_MAX - 1); i++) {
243 		iov[iovlen].iov_base = (caddr_t)&pl[i]->len;
244 		explen += iov[iovlen++].iov_len  = sizeof (pl[i]->len);
245 		iov[iovlen].iov_base = (caddr_t)pl[i]->pkt;
246 		explen += iov[iovlen++].iov_len  = pl[i]->len;
247 	}
248 
249 	retval = writev(fd, iov, iovlen);
250 
251 	(void) close(fd);
252 
253 	if (retval != explen)
254 		return (-1);
255 
256 	return (0);
257 }
258 
259 /*
260  * relativize_time(): re-relativizes a time in a DHCP option
261  *
262  *   input: DHCP_OPT *: the DHCP option parameter to convert
263  *	    time_t: the time the leases in the packet are currently relative to
264  *	    time_t: the current time which leases will become relative to
265  *  output: void
266  */
267 
268 static void
269 relativize_time(DHCP_OPT *option, time_t orig_time, time_t current_time)
270 {
271 	uint32_t	pkt_time;
272 	time_t		time_diff = current_time - orig_time;
273 
274 	if (option == NULL || option->len != sizeof (lease_t))
275 		return;
276 
277 	(void) memcpy(&pkt_time, option->value, option->len);
278 	if (ntohl(pkt_time) != DHCP_PERM)
279 		pkt_time = htonl(ntohl(pkt_time) - time_diff);
280 
281 	(void) memcpy(option->value, &pkt_time, option->len);
282 }
283