Home | History | Annotate | Download | only in common
      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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <unistd.h>
     31 #include <sys/socket.h>
     32 #include <netinet/in.h>
     33 #include <net/if.h>
     34 #include <netinet/dhcp.h>
     35 #include <sys/types.h>
     36 #include <sys/stat.h>
     37 #include <time.h>
     38 #include <string.h>			/* memcpy */
     39 #include <fcntl.h>
     40 #include <limits.h>
     41 
     42 #include "dhcp_hostconf.h"
     43 
     44 static void		relativize_time(DHCP_OPT *, time_t, time_t);
     45 static void		relativize_v6(uint32_t *, 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  *	    boolean_t: B_TRUE if using DHCPv6
     53  *  output: char *: the hostconf filename
     54  *    note: uses an internal static buffer (not threadsafe)
     55  */
     56 
     57 char *
     58 ifname_to_hostconf(const char *ifname, boolean_t isv6)
     59 {
     60 	static char filename[sizeof (DHCP_HOSTCONF_TMPL6) + LIFNAMSIZ];
     61 
     62 	(void) snprintf(filename, sizeof (filename), "%s%s%s",
     63 	    DHCP_HOSTCONF_PREFIX, ifname,
     64 	    isv6 ? DHCP_HOSTCONF_SUFFIX6 : DHCP_HOSTCONF_SUFFIX);
     65 
     66 	return (filename);
     67 }
     68 
     69 /*
     70  * remove_hostconf(): removes an interface.dhc file
     71  *
     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
     75  *          (errno is set)
     76  */
     77 
     78 int
     79 remove_hostconf(const char *ifname, boolean_t isv6)
     80 {
     81 	return (unlink(ifname_to_hostconf(ifname, isv6)));
     82 }
     83 
     84 /*
     85  * read_hostconf(): reads the contents of an <if>.dhc file into a PKT_LIST
     86  *
     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
     94  */
     95 
     96 int
     97 read_hostconf(const char *ifname, PKT_LIST **plpp, uint_t plplen,
     98     boolean_t isv6)
     99 {
    100 	PKT_LIST	*plp = NULL;
    101 	PKT		*pkt = NULL;
    102 	int		fd;
    103 	time_t		orig_time, current_time = time(NULL);
    104 	uint32_t	lease;
    105 	uint32_t	magic;
    106 	int		pcnt = 0;
    107 	int		retval;
    108 
    109 	fd = open(ifname_to_hostconf(ifname, isv6), O_RDONLY);
    110 	if (fd == -1)
    111 		return (-1);
    112 
    113 	if (read(fd, &magic, sizeof (magic)) != sizeof (magic))
    114 		goto failure;
    115 
    116 	if (magic != (isv6 ? DHCP_HOSTCONF_MAGIC6 : DHCP_HOSTCONF_MAGIC))
    117 		goto failure;
    118 
    119 	if (read(fd, &orig_time, sizeof (orig_time)) != sizeof (orig_time))
    120 		goto failure;
    121 
    122 	/*
    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.
    126 	 */
    127 
    128 	for (pcnt = 0; pcnt < plplen; pcnt++) {
    129 
    130 		plp = NULL;
    131 		pkt = NULL;
    132 
    133 		if ((plp = calloc(1, sizeof (PKT_LIST))) == NULL)
    134 			goto failure;
    135 
    136 		retval = read(fd, &plp->len, sizeof (plp->len));
    137 		if (retval == 0 && pcnt != 0) {
    138 			/*
    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.
    143 			 */
    144 			free(plp);
    145 			break;
    146 		} else if (retval != sizeof (plp->len))
    147 			goto failure;
    148 
    149 		if ((pkt = malloc(plp->len)) == NULL)
    150 			goto failure;
    151 
    152 		if (read(fd, pkt, plp->len) != plp->len)
    153 			goto failure;
    154 
    155 		plp->pkt = pkt;
    156 
    157 		plpp[pcnt] = plp;
    158 
    159 		if (!isv6 && dhcp_options_scan(plp, B_TRUE) != 0)
    160 			goto failure;
    161 
    162 		/*
    163 		 * First packet used to validate that we're interested,
    164 		 * the rest are presumed to be historical reference and
    165 		 * are not relativized
    166 		 */
    167 		if (pcnt == 0)
    168 			continue;
    169 
    170 		if (isv6) {
    171 			dhcpv6_option_t	d6o;
    172 			dhcpv6_ia_na_t	d6in;
    173 			dhcpv6_iaaddr_t	d6ia;
    174 			uchar_t		*opts, *optmax, *subomax;
    175 
    176 			/*
    177 			 * Loop over contents of the packet to find the address
    178 			 * options.
    179 			 */
    180 			opts = (uchar_t *)pkt + sizeof (dhcpv6_message_t);
    181 			optmax = (uchar_t *)pkt + plp->len;
    182 			while (opts + sizeof (d6o) <= optmax) {
    183 
    184 				/*
    185 				 * Extract option header and make sure option
    186 				 * is intact.
    187 				 */
    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)
    193 					break;
    194 
    195 				/*
    196 				 * If this isn't an option that contains
    197 				 * address or prefix leases, then skip over it.
    198 				 */
    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) {
    202 					opts = subomax;
    203 					continue;
    204 				}
    205 
    206 				/*
    207 				 * Handle the option first.
    208 				 */
    209 				if (d6o.d6o_code == DHCPV6_OPT_IA_TA) {
    210 					/* no timers in this structure */
    211 					opts += sizeof (dhcpv6_ia_ta_t);
    212 				} else {
    213 					/* both na and pd */
    214 					if (opts + sizeof (d6in) > subomax) {
    215 						opts = subomax;
    216 						continue;
    217 					}
    218 					(void) memcpy(&d6in, opts,
    219 					    sizeof (d6in));
    220 					relativize_v6(&d6in.d6in_t1, orig_time,
    221 					    current_time);
    222 					relativize_v6(&d6in.d6in_t2, orig_time,
    223 					    current_time);
    224 					(void) memcpy(opts, &d6in,
    225 					    sizeof (d6in));
    226 					opts += sizeof (d6in);
    227 				}
    228 
    229 				/*
    230 				 * Now handle each suboption (address) inside.
    231 				 */
    232 				while (opts + sizeof (d6o) <= subomax) {
    233 					/*
    234 					 * Verify the suboption header first.
    235 					 */
    236 					(void) memcpy(&d6o, opts,
    237 					    sizeof (d6o));
    238 					d6o.d6o_code = ntohs(d6o.d6o_code);
    239 					d6o.d6o_len = ntohs(d6o.d6o_len);
    240 					if (opts + sizeof (d6o) + d6o.d6o_len >
    241 					    subomax)
    242 						break;
    243 					if (d6o.d6o_code != DHCPV6_OPT_IAADDR) {
    244 						opts += sizeof (d6o) +
    245 						    d6o.d6o_len;
    246 						continue;
    247 					}
    248 
    249 					/*
    250 					 * Now process the contents.
    251 					 */
    252 					if (opts + sizeof (d6ia) > subomax)
    253 						break;
    254 					(void) memcpy(&d6ia, opts,
    255 					    sizeof (d6ia));
    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,
    261 					    sizeof (d6ia));
    262 					opts += sizeof (d6o) + d6o.d6o_len;
    263 				}
    264 				opts = subomax;
    265 			}
    266 		} else {
    267 
    268 			/*
    269 			 * make sure the IPv4 DHCP lease is still valid.
    270 			 */
    271 
    272 			if (plp->opts[CD_LEASE_TIME] != NULL &&
    273 			    plp->opts[CD_LEASE_TIME]->len ==
    274 			    sizeof (lease_t)) {
    275 
    276 				(void) memcpy(&lease,
    277 				    plp->opts[CD_LEASE_TIME]->value,
    278 				    sizeof (lease_t));
    279 
    280 				lease = ntohl(lease);
    281 				if ((lease != DHCP_PERM) &&
    282 				    (orig_time + lease) <= current_time)
    283 					goto failure;
    284 			}
    285 
    286 			relativize_time(plp->opts[CD_T1_TIME], orig_time,
    287 			    current_time);
    288 			relativize_time(plp->opts[CD_T2_TIME], orig_time,
    289 			    current_time);
    290 			relativize_time(plp->opts[CD_LEASE_TIME], orig_time,
    291 			    current_time);
    292 		}
    293 	}
    294 
    295 	(void) close(fd);
    296 	return (pcnt);
    297 
    298 failure:
    299 	free(pkt);
    300 	free(plp);
    301 	while (pcnt-- > 0) {
    302 		free(plpp[pcnt]->pkt);
    303 		free(plpp[pcnt]);
    304 	}
    305 	(void) close(fd);
    306 	return (-1);
    307 }
    308 
    309 /*
    310  * write_hostconf(): writes the contents of a PKT_LIST into an <if>.dhc file
    311  *
    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
    319  *	    (errno is set)
    320  */
    321 
    322 int
    323 write_hostconf(
    324     const char *ifname,
    325     PKT_LIST *pl[],
    326     uint_t pllen,
    327     time_t relative_to,
    328     boolean_t isv6)
    329 {
    330 	int		fd;
    331 	struct iovec	iov[IOV_MAX];
    332 	int		retval;
    333 	uint32_t	magic;
    334 	ssize_t		explen = 0; /* Expected length of write */
    335 	int		i, iovlen = 0;
    336 
    337 	fd = open(ifname_to_hostconf(ifname, isv6), O_WRONLY|O_CREAT|O_TRUNC,
    338 	    0600);
    339 	if (fd == -1)
    340 		return (-1);
    341 
    342 	/*
    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.
    347 	 */
    348 
    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;
    359 	}
    360 
    361 	retval = writev(fd, iov, iovlen);
    362 
    363 	(void) close(fd);
    364 
    365 	if (retval != explen)
    366 		return (-1);
    367 
    368 	return (0);
    369 }
    370 
    371 /*
    372  * relativize_time(): re-relativizes a time in a DHCP option
    373  *
    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
    377  *  output: void
    378  */
    379 
    380 static void
    381 relativize_time(DHCP_OPT *option, time_t orig_time, time_t current_time)
    382 {
    383 	uint32_t	pkt_time;
    384 	time_t		time_diff = current_time - orig_time;
    385 
    386 	if (option == NULL || option->len != sizeof (lease_t))
    387 		return;
    388 
    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);
    392 
    393 	(void) memcpy(option->value, &pkt_time, option->len);
    394 }
    395 
    396 /*
    397  * relativize_v6(): re-relativizes a time in a DHCPv6 option
    398  *
    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
    402  *  output: void
    403  */
    404 
    405 static void
    406 relativize_v6(uint32_t *val, time_t orig_time, time_t current_time)
    407 {
    408 	uint32_t	hval;
    409 	time_t		time_diff = current_time - orig_time;
    410 
    411 	hval = ntohl(*val);
    412 	if (hval != DHCPV6_INFTIME) {
    413 		if (hval < time_diff)
    414 			*val = 0;
    415 		else
    416 			*val = htonl(hval - time_diff);
    417 	}
    418 }
    419