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 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <net/if.h>
32 #include <netinet/dhcp.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <time.h>
36 #include <string.h> /* memcpy */
37 #include <fcntl.h>
38 #include <limits.h>
39
40 #include "dhcp_hostconf.h"
41
42 static void relativize_time(DHCP_OPT *, time_t, time_t);
43 static void relativize_v6(uint32_t *, time_t, time_t);
44
45 /*
46 * ifname_to_hostconf(): converts an interface name into a hostconf file for
47 * that interface
48 *
49 * input: const char *: the interface name
50 * boolean_t: B_TRUE if using DHCPv6
51 * output: char *: the hostconf filename
52 * note: uses an internal static buffer (not threadsafe)
53 */
54
55 char *
ifname_to_hostconf(const char * ifname,boolean_t isv6)56 ifname_to_hostconf(const char *ifname, boolean_t isv6)
57 {
58 static char filename[sizeof (DHCP_HOSTCONF_TMPL6) + LIFNAMSIZ];
59
60 (void) snprintf(filename, sizeof (filename), "%s%s%s",
61 DHCP_HOSTCONF_PREFIX, ifname,
62 isv6 ? DHCP_HOSTCONF_SUFFIX6 : 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 * boolean_t: B_TRUE if using DHCPv6
72 * output: int: 0 if the file is removed, -1 if it can't be removed
73 * (errno is set)
74 */
75
76 int
remove_hostconf(const char * ifname,boolean_t isv6)77 remove_hostconf(const char *ifname, boolean_t isv6)
78 {
79 return (unlink(ifname_to_hostconf(ifname, isv6)));
80 }
81
82 /*
83 * read_hostconf(): reads the contents of an <if>.dhc file into a PKT_LIST
84 *
85 * input: const char *: the interface name
86 * PKT_LIST **: a pointer to a PKT_LIST * to store the info in
87 * uint_t: the length of the list of PKT_LISTs
88 * boolean_t: B_TRUE if using DHCPv6
89 * output: int: >0 if the file is read and loaded into the PKT_LIST *
90 * successfully, -1 otherwise (errno is set)
91 * note: the PKT and PKT_LISTs are dynamically allocated here
92 */
93
94 int
read_hostconf(const char * ifname,PKT_LIST ** plpp,uint_t plplen,boolean_t isv6)95 read_hostconf(const char *ifname, PKT_LIST **plpp, uint_t plplen,
96 boolean_t isv6)
97 {
98 PKT_LIST *plp = NULL;
99 PKT *pkt = NULL;
100 int fd;
101 time_t orig_time, current_time = time(NULL);
102 uint32_t lease;
103 uint32_t magic;
104 int pcnt = 0;
105 int retval;
106
107 fd = open(ifname_to_hostconf(ifname, isv6), O_RDONLY);
108 if (fd == -1)
109 return (-1);
110
111 if (read(fd, &magic, sizeof (magic)) != sizeof (magic))
112 goto failure;
113
114 if (magic != (isv6 ? DHCP_HOSTCONF_MAGIC6 : DHCP_HOSTCONF_MAGIC))
115 goto failure;
116
117 if (read(fd, &orig_time, sizeof (orig_time)) != sizeof (orig_time))
118 goto failure;
119
120 /*
121 * read the packet back in from disk, and for v4, run it through
122 * dhcp_options_scan(). note that we use calloc() because
123 * dhcp_options_scan() relies on the structure being zeroed.
124 */
125
126 for (pcnt = 0; pcnt < plplen; pcnt++) {
127
128 plp = NULL;
129 pkt = NULL;
130
131 if ((plp = calloc(1, sizeof (PKT_LIST))) == NULL)
132 goto failure;
133
134 retval = read(fd, &plp->len, sizeof (plp->len));
135 if (retval == 0 && pcnt != 0) {
136 /*
137 * Reached end of file on a boundary, but after
138 * we've read at least one packet, so we consider
139 * this successful, allowing us to use files from
140 * older versions of the agent happily.
141 */
142 free(plp);
143 break;
144 } else if (retval != sizeof (plp->len))
145 goto failure;
146
147 if ((pkt = malloc(plp->len)) == NULL)
148 goto failure;
149
150 if (read(fd, pkt, plp->len) != plp->len)
151 goto failure;
152
153 plp->pkt = pkt;
154
155 plpp[pcnt] = plp;
156
157 if (!isv6 && dhcp_options_scan(plp, B_TRUE) != 0)
158 goto failure;
159
160 /*
161 * First packet used to validate that we're interested,
162 * the rest are presumed to be historical reference and
163 * are not relativized
164 */
165 if (pcnt == 0)
166 continue;
167
168 if (isv6) {
169 dhcpv6_option_t d6o;
170 dhcpv6_ia_na_t d6in;
171 dhcpv6_iaaddr_t d6ia;
172 uchar_t *opts, *optmax, *subomax;
173
174 /*
175 * Loop over contents of the packet to find the address
176 * options.
177 */
178 opts = (uchar_t *)pkt + sizeof (dhcpv6_message_t);
179 optmax = (uchar_t *)pkt + plp->len;
180 while (opts + sizeof (d6o) <= optmax) {
181
182 /*
183 * Extract option header and make sure option
184 * is intact.
185 */
186 (void) memcpy(&d6o, opts, sizeof (d6o));
187 d6o.d6o_code = ntohs(d6o.d6o_code);
188 d6o.d6o_len = ntohs(d6o.d6o_len);
189 subomax = opts + sizeof (d6o) + d6o.d6o_len;
190 if (subomax > optmax)
191 break;
192
193 /*
194 * If this isn't an option that contains
195 * address or prefix leases, then skip over it.
196 */
197 if (d6o.d6o_code != DHCPV6_OPT_IA_NA &&
198 d6o.d6o_code != DHCPV6_OPT_IA_TA &&
199 d6o.d6o_code != DHCPV6_OPT_IA_PD) {
200 opts = subomax;
201 continue;
202 }
203
204 /*
205 * Handle the option first.
206 */
207 if (d6o.d6o_code == DHCPV6_OPT_IA_TA) {
208 /* no timers in this structure */
209 opts += sizeof (dhcpv6_ia_ta_t);
210 } else {
211 /* both na and pd */
212 if (opts + sizeof (d6in) > subomax) {
213 opts = subomax;
214 continue;
215 }
216 (void) memcpy(&d6in, opts,
217 sizeof (d6in));
218 relativize_v6(&d6in.d6in_t1, orig_time,
219 current_time);
220 relativize_v6(&d6in.d6in_t2, orig_time,
221 current_time);
222 (void) memcpy(opts, &d6in,
223 sizeof (d6in));
224 opts += sizeof (d6in);
225 }
226
227 /*
228 * Now handle each suboption (address) inside.
229 */
230 while (opts + sizeof (d6o) <= subomax) {
231 /*
232 * Verify the suboption header first.
233 */
234 (void) memcpy(&d6o, opts,
235 sizeof (d6o));
236 d6o.d6o_code = ntohs(d6o.d6o_code);
237 d6o.d6o_len = ntohs(d6o.d6o_len);
238 if (opts + sizeof (d6o) + d6o.d6o_len >
239 subomax)
240 break;
241 if (d6o.d6o_code != DHCPV6_OPT_IAADDR) {
242 opts += sizeof (d6o) +
243 d6o.d6o_len;
244 continue;
245 }
246
247 /*
248 * Now process the contents.
249 */
250 if (opts + sizeof (d6ia) > subomax)
251 break;
252 (void) memcpy(&d6ia, opts,
253 sizeof (d6ia));
254 relativize_v6(&d6ia.d6ia_preflife,
255 orig_time, current_time);
256 relativize_v6(&d6ia.d6ia_vallife,
257 orig_time, current_time);
258 (void) memcpy(opts, &d6ia,
259 sizeof (d6ia));
260 opts += sizeof (d6o) + d6o.d6o_len;
261 }
262 opts = subomax;
263 }
264 } else {
265
266 /*
267 * make sure the IPv4 DHCP lease is still valid.
268 */
269
270 if (plp->opts[CD_LEASE_TIME] != NULL &&
271 plp->opts[CD_LEASE_TIME]->len ==
272 sizeof (lease_t)) {
273
274 (void) memcpy(&lease,
275 plp->opts[CD_LEASE_TIME]->value,
276 sizeof (lease_t));
277
278 lease = ntohl(lease);
279 if ((lease != DHCP_PERM) &&
280 (orig_time + lease) <= current_time)
281 goto failure;
282 }
283
284 relativize_time(plp->opts[CD_T1_TIME], orig_time,
285 current_time);
286 relativize_time(plp->opts[CD_T2_TIME], orig_time,
287 current_time);
288 relativize_time(plp->opts[CD_LEASE_TIME], orig_time,
289 current_time);
290 }
291 }
292
293 (void) close(fd);
294 return (pcnt);
295
296 failure:
297 free(pkt);
298 free(plp);
299 while (pcnt-- > 0) {
300 free(plpp[pcnt]->pkt);
301 free(plpp[pcnt]);
302 }
303 (void) close(fd);
304 return (-1);
305 }
306
307 /*
308 * write_hostconf(): writes the contents of a PKT_LIST into an <if>.dhc file
309 *
310 * input: const char *: the interface name
311 * PKT_LIST **: a list of pointers to PKT_LIST to write
312 * uint_t: length of the list of PKT_LIST pointers
313 * time_t: a starting time to treat the relative lease times
314 * in the first packet as relative to
315 * boolean_t: B_TRUE if using DHCPv6
316 * output: int: 0 if the file is written successfully, -1 otherwise
317 * (errno is set)
318 */
319
320 int
write_hostconf(const char * ifname,PKT_LIST * pl[],uint_t pllen,time_t relative_to,boolean_t isv6)321 write_hostconf(
322 const char *ifname,
323 PKT_LIST *pl[],
324 uint_t pllen,
325 time_t relative_to,
326 boolean_t isv6)
327 {
328 int fd;
329 struct iovec iov[IOV_MAX];
330 int retval;
331 uint32_t magic;
332 ssize_t explen = 0; /* Expected length of write */
333 int i, iovlen = 0;
334
335 fd = open(ifname_to_hostconf(ifname, isv6), O_WRONLY|O_CREAT|O_TRUNC,
336 0600);
337 if (fd == -1)
338 return (-1);
339
340 /*
341 * first write our magic number, then the relative time of the
342 * leases, then for each packet we write the length of the packet
343 * followed by the packet. we will then use the relative time in
344 * read_hostconf() to recalculate the lease times for the first packet.
345 */
346
347 magic = isv6 ? DHCP_HOSTCONF_MAGIC6 : DHCP_HOSTCONF_MAGIC;
348 iov[iovlen].iov_base = (caddr_t)&magic;
349 explen += iov[iovlen++].iov_len = sizeof (magic);
350 iov[iovlen].iov_base = (caddr_t)&relative_to;
351 explen += iov[iovlen++].iov_len = sizeof (relative_to);
352 for (i = 0; i < pllen && iovlen < (IOV_MAX - 1); i++) {
353 iov[iovlen].iov_base = (caddr_t)&pl[i]->len;
354 explen += iov[iovlen++].iov_len = sizeof (pl[i]->len);
355 iov[iovlen].iov_base = (caddr_t)pl[i]->pkt;
356 explen += iov[iovlen++].iov_len = pl[i]->len;
357 }
358
359 retval = writev(fd, iov, iovlen);
360
361 (void) close(fd);
362
363 if (retval != explen)
364 return (-1);
365
366 return (0);
367 }
368
369 /*
370 * relativize_time(): re-relativizes a time in a DHCP option
371 *
372 * input: DHCP_OPT *: the DHCP option parameter to convert
373 * time_t: the time the leases in the packet are currently relative to
374 * time_t: the current time which leases will become relative to
375 * output: void
376 */
377
378 static void
relativize_time(DHCP_OPT * option,time_t orig_time,time_t current_time)379 relativize_time(DHCP_OPT *option, time_t orig_time, time_t current_time)
380 {
381 uint32_t pkt_time;
382 time_t time_diff = current_time - orig_time;
383
384 if (option == NULL || option->len != sizeof (lease_t))
385 return;
386
387 (void) memcpy(&pkt_time, option->value, option->len);
388 if (ntohl(pkt_time) != DHCP_PERM)
389 pkt_time = htonl(ntohl(pkt_time) - time_diff);
390
391 (void) memcpy(option->value, &pkt_time, option->len);
392 }
393
394 /*
395 * relativize_v6(): re-relativizes a time in a DHCPv6 option
396 *
397 * input: uint32_t *: the time value to convert
398 * time_t: the time the leases in the packet are currently relative to
399 * time_t: the current time which leases will become relative to
400 * output: void
401 */
402
403 static void
relativize_v6(uint32_t * val,time_t orig_time,time_t current_time)404 relativize_v6(uint32_t *val, time_t orig_time, time_t current_time)
405 {
406 uint32_t hval;
407 time_t time_diff = current_time - orig_time;
408
409 hval = ntohl(*val);
410 if (hval != DHCPV6_INFTIME) {
411 if (hval < time_diff)
412 *val = 0;
413 else
414 *val = htonl(hval - time_diff);
415 }
416 }
417