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