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 * 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 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 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 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 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 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