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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
31 #include <sys/socket.h>
32 #include <netinet/in.h>
34 #include <netinet/dhcp.h>
35 #include <sys/types.h>
38 #include <string.h> /* memcpy */
42 #include "dhcp_hostconf.h"
44 static void relativize_time(DHCP_OPT
*, time_t, time_t);
45 static void relativize_v6(uint32_t *, time_t, time_t);
48 * ifname_to_hostconf(): converts an interface name into a hostconf file for
51 * input: const char *: the interface name
52 * boolean_t: B_TRUE if using DHCPv6
53 * output: char *: the hostconf filename
54 * note: uses an internal static buffer (not threadsafe)
58 ifname_to_hostconf(const char *ifname
, boolean_t isv6
)
60 static char filename
[sizeof (DHCP_HOSTCONF_TMPL6
) + LIFNAMSIZ
];
62 (void) snprintf(filename
, sizeof (filename
), "%s%s%s",
63 DHCP_HOSTCONF_PREFIX
, ifname
,
64 isv6
? DHCP_HOSTCONF_SUFFIX6
: DHCP_HOSTCONF_SUFFIX
);
70 * remove_hostconf(): removes an interface.dhc file
72 * input: const char *: the interface name
73 * boolean_t: B_TRUE if using DHCPv6
74 * output: int: 0 if the file is removed, -1 if it can't be removed
79 remove_hostconf(const char *ifname
, boolean_t isv6
)
81 return (unlink(ifname_to_hostconf(ifname
, isv6
)));
85 * read_hostconf(): reads the contents of an <if>.dhc file into a PKT_LIST
87 * input: const char *: the interface name
88 * PKT_LIST **: a pointer to a PKT_LIST * to store the info in
89 * uint_t: the length of the list of PKT_LISTs
90 * boolean_t: B_TRUE if using DHCPv6
91 * output: int: >0 if the file is read and loaded into the PKT_LIST *
92 * successfully, -1 otherwise (errno is set)
93 * note: the PKT and PKT_LISTs are dynamically allocated here
97 read_hostconf(const char *ifname
, PKT_LIST
**plpp
, uint_t plplen
,
100 PKT_LIST
*plp
= NULL
;
103 time_t orig_time
, current_time
= time(NULL
);
109 fd
= open(ifname_to_hostconf(ifname
, isv6
), O_RDONLY
);
113 if (read(fd
, &magic
, sizeof (magic
)) != sizeof (magic
))
116 if (magic
!= (isv6
? DHCP_HOSTCONF_MAGIC6
: DHCP_HOSTCONF_MAGIC
))
119 if (read(fd
, &orig_time
, sizeof (orig_time
)) != sizeof (orig_time
))
123 * read the packet back in from disk, and for v4, run it through
124 * dhcp_options_scan(). note that we use calloc() because
125 * dhcp_options_scan() relies on the structure being zeroed.
128 for (pcnt
= 0; pcnt
< plplen
; pcnt
++) {
133 if ((plp
= calloc(1, sizeof (PKT_LIST
))) == NULL
)
136 retval
= read(fd
, &plp
->len
, sizeof (plp
->len
));
137 if (retval
== 0 && pcnt
!= 0) {
139 * Reached end of file on a boundary, but after
140 * we've read at least one packet, so we consider
141 * this successful, allowing us to use files from
142 * older versions of the agent happily.
146 } else if (retval
!= sizeof (plp
->len
))
149 if ((pkt
= malloc(plp
->len
)) == NULL
)
152 if (read(fd
, pkt
, plp
->len
) != plp
->len
)
159 if (!isv6
&& dhcp_options_scan(plp
, B_TRUE
) != 0)
163 * First packet used to validate that we're interested,
164 * the rest are presumed to be historical reference and
165 * are not relativized
173 dhcpv6_iaaddr_t d6ia
;
174 uchar_t
*opts
, *optmax
, *subomax
;
177 * Loop over contents of the packet to find the address
180 opts
= (uchar_t
*)pkt
+ sizeof (dhcpv6_message_t
);
181 optmax
= (uchar_t
*)pkt
+ plp
->len
;
182 while (opts
+ sizeof (d6o
) <= optmax
) {
185 * Extract option header and make sure option
188 (void) memcpy(&d6o
, opts
, sizeof (d6o
));
189 d6o
.d6o_code
= ntohs(d6o
.d6o_code
);
190 d6o
.d6o_len
= ntohs(d6o
.d6o_len
);
191 subomax
= opts
+ sizeof (d6o
) + d6o
.d6o_len
;
192 if (subomax
> optmax
)
196 * If this isn't an option that contains
197 * address or prefix leases, then skip over it.
199 if (d6o
.d6o_code
!= DHCPV6_OPT_IA_NA
&&
200 d6o
.d6o_code
!= DHCPV6_OPT_IA_TA
&&
201 d6o
.d6o_code
!= DHCPV6_OPT_IA_PD
) {
207 * Handle the option first.
209 if (d6o
.d6o_code
== DHCPV6_OPT_IA_TA
) {
210 /* no timers in this structure */
211 opts
+= sizeof (dhcpv6_ia_ta_t
);
214 if (opts
+ sizeof (d6in
) > subomax
) {
218 (void) memcpy(&d6in
, opts
,
220 relativize_v6(&d6in
.d6in_t1
, orig_time
,
222 relativize_v6(&d6in
.d6in_t2
, orig_time
,
224 (void) memcpy(opts
, &d6in
,
226 opts
+= sizeof (d6in
);
230 * Now handle each suboption (address) inside.
232 while (opts
+ sizeof (d6o
) <= subomax
) {
234 * Verify the suboption header first.
236 (void) memcpy(&d6o
, opts
,
238 d6o
.d6o_code
= ntohs(d6o
.d6o_code
);
239 d6o
.d6o_len
= ntohs(d6o
.d6o_len
);
240 if (opts
+ sizeof (d6o
) + d6o
.d6o_len
>
243 if (d6o
.d6o_code
!= DHCPV6_OPT_IAADDR
) {
244 opts
+= sizeof (d6o
) +
250 * Now process the contents.
252 if (opts
+ sizeof (d6ia
) > subomax
)
254 (void) memcpy(&d6ia
, opts
,
256 relativize_v6(&d6ia
.d6ia_preflife
,
257 orig_time
, current_time
);
258 relativize_v6(&d6ia
.d6ia_vallife
,
259 orig_time
, current_time
);
260 (void) memcpy(opts
, &d6ia
,
262 opts
+= sizeof (d6o
) + d6o
.d6o_len
;
269 * make sure the IPv4 DHCP lease is still valid.
272 if (plp
->opts
[CD_LEASE_TIME
] != NULL
&&
273 plp
->opts
[CD_LEASE_TIME
]->len
==
276 (void) memcpy(&lease
,
277 plp
->opts
[CD_LEASE_TIME
]->value
,
280 lease
= ntohl(lease
);
281 if ((lease
!= DHCP_PERM
) &&
282 (orig_time
+ lease
) <= current_time
)
286 relativize_time(plp
->opts
[CD_T1_TIME
], orig_time
,
288 relativize_time(plp
->opts
[CD_T2_TIME
], orig_time
,
290 relativize_time(plp
->opts
[CD_LEASE_TIME
], orig_time
,
302 free(plpp
[pcnt
]->pkt
);
310 * write_hostconf(): writes the contents of a PKT_LIST into an <if>.dhc file
312 * input: const char *: the interface name
313 * PKT_LIST **: a list of pointers to PKT_LIST to write
314 * uint_t: length of the list of PKT_LIST pointers
315 * time_t: a starting time to treat the relative lease times
316 * in the first packet as relative to
317 * boolean_t: B_TRUE if using DHCPv6
318 * output: int: 0 if the file is written successfully, -1 otherwise
331 struct iovec iov
[IOV_MAX
];
334 ssize_t explen
= 0; /* Expected length of write */
337 fd
= open(ifname_to_hostconf(ifname
, isv6
), O_WRONLY
|O_CREAT
|O_TRUNC
,
343 * first write our magic number, then the relative time of the
344 * leases, then for each packet we write the length of the packet
345 * followed by the packet. we will then use the relative time in
346 * read_hostconf() to recalculate the lease times for the first packet.
349 magic
= isv6
? DHCP_HOSTCONF_MAGIC6
: DHCP_HOSTCONF_MAGIC
;
350 iov
[iovlen
].iov_base
= (caddr_t
)&magic
;
351 explen
+= iov
[iovlen
++].iov_len
= sizeof (magic
);
352 iov
[iovlen
].iov_base
= (caddr_t
)&relative_to
;
353 explen
+= iov
[iovlen
++].iov_len
= sizeof (relative_to
);
354 for (i
= 0; i
< pllen
&& iovlen
< (IOV_MAX
- 1); i
++) {
355 iov
[iovlen
].iov_base
= (caddr_t
)&pl
[i
]->len
;
356 explen
+= iov
[iovlen
++].iov_len
= sizeof (pl
[i
]->len
);
357 iov
[iovlen
].iov_base
= (caddr_t
)pl
[i
]->pkt
;
358 explen
+= iov
[iovlen
++].iov_len
= pl
[i
]->len
;
361 retval
= writev(fd
, iov
, iovlen
);
365 if (retval
!= explen
)
372 * relativize_time(): re-relativizes a time in a DHCP option
374 * input: DHCP_OPT *: the DHCP option parameter to convert
375 * time_t: the time the leases in the packet are currently relative to
376 * time_t: the current time which leases will become relative to
381 relativize_time(DHCP_OPT
*option
, time_t orig_time
, time_t current_time
)
384 time_t time_diff
= current_time
- orig_time
;
386 if (option
== NULL
|| option
->len
!= sizeof (lease_t
))
389 (void) memcpy(&pkt_time
, option
->value
, option
->len
);
390 if (ntohl(pkt_time
) != DHCP_PERM
)
391 pkt_time
= htonl(ntohl(pkt_time
) - time_diff
);
393 (void) memcpy(option
->value
, &pkt_time
, option
->len
);
397 * relativize_v6(): re-relativizes a time in a DHCPv6 option
399 * input: uint32_t *: the time value to convert
400 * time_t: the time the leases in the packet are currently relative to
401 * time_t: the current time which leases will become relative to
406 relativize_v6(uint32_t *val
, time_t orig_time
, time_t current_time
)
409 time_t time_diff
= current_time
- orig_time
;
412 if (hval
!= DHCPV6_INFTIME
) {
413 if (hval
< time_diff
)
416 *val
= htonl(hval
- time_diff
);