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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <sys/socket.h> 33 #include <netinet/in.h> 34 #include <net/if.h> 35 #include <netinet/dhcp.h> 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 #include <time.h> 39 #include <string.h> /* memcpy */ 40 #include <fcntl.h> 41 #include <limits.h> 42 43 #include "dhcp_hostconf.h" 44 45 static void relativize_time(DHCP_OPT *, 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 * output: char *: the hostconf filename 53 * note: uses an internal static buffer (not threadsafe) 54 */ 55 56 char * 57 ifname_to_hostconf(const char *ifname) 58 { 59 static char filename[sizeof (DHCP_HOSTCONF_TMPL) + IFNAMSIZ]; 60 61 (void) snprintf(filename, sizeof (filename), "%s%s%s", 62 DHCP_HOSTCONF_PREFIX, ifname, 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 * output: int: 0 if the file is removed, -1 if it can't be removed 72 * (errno is set) 73 */ 74 75 int 76 remove_hostconf(const char *ifname) 77 { 78 return (unlink(ifname_to_hostconf(ifname))); 79 } 80 81 /* 82 * read_hostconf(): reads the contents of an <if>.dhc file into a PKT_LIST 83 * 84 * input: const char *: the interface name 85 * PKT_LIST **: a pointer to a PKT_LIST * to store the info in 86 * uint_t: the length of the list of PKT_LISTs 87 * output: int: 0 if the file is read and loaded into the PKT_LIST * 88 * successfully, -1 otherwise (errno is set) 89 * note: the PKT and PKT_LISTs are dynamically allocated here 90 */ 91 92 int 93 read_hostconf(const char *ifname, PKT_LIST **plpp, uint_t plplen) 94 { 95 PKT_LIST *plp = NULL; 96 PKT *pkt = NULL; 97 int fd; 98 time_t orig_time, current_time = time(NULL); 99 uint32_t lease; 100 uint32_t magic; 101 int pcnt = 0; 102 int retval; 103 104 fd = open(ifname_to_hostconf(ifname), O_RDONLY); 105 if (fd == -1) 106 return (-1); 107 108 if (read(fd, &magic, sizeof (magic)) != sizeof (magic)) 109 goto failure; 110 111 if (magic != DHCP_HOSTCONF_MAGIC) 112 goto failure; 113 114 if (read(fd, &orig_time, sizeof (orig_time)) != sizeof (orig_time)) 115 goto failure; 116 117 /* 118 * read the packet back in from disk, and run it through 119 * dhcp_options_scan(). note that we use calloc() since 120 * dhcp_options_scan() relies on the packet being zeroed. 121 */ 122 123 for (pcnt = 0; pcnt < plplen; pcnt++) { 124 125 plp = NULL; 126 pkt = NULL; 127 128 if ((plp = calloc(1, sizeof (PKT_LIST))) == NULL) 129 goto failure; 130 131 retval = read(fd, &plp->len, sizeof (plp->len)); 132 if (retval == 0 && pcnt != 0) { 133 /* 134 * Reached end of file on a boundary, but after 135 * we've read at least one packet, so we consider 136 * this successful, allowing us to use files from 137 * older versions of the agent happily. 138 */ 139 free(plp); 140 break; 141 } else if (retval != sizeof (plp->len)) 142 goto failure; 143 144 if ((pkt = malloc(plp->len)) == NULL) 145 goto failure; 146 147 if (read(fd, pkt, plp->len) != plp->len) 148 goto failure; 149 150 plp->pkt = pkt; 151 152 plpp[pcnt] = plp; 153 154 if (dhcp_options_scan(plp, B_TRUE) != 0) 155 goto failure; 156 157 /* 158 * First packet used to validate that we're interested, 159 * the rest are presumed to be historical reference and 160 * are not relativized 161 */ 162 if (pcnt == 0) 163 continue; 164 165 /* 166 * make sure the lease is still valid. 167 */ 168 169 if (plp->opts[CD_LEASE_TIME] != NULL && 170 plp->opts[CD_LEASE_TIME]->len == sizeof (lease_t)) { 171 172 (void) memcpy(&lease, plp->opts[CD_LEASE_TIME]->value, 173 sizeof (lease_t)); 174 175 lease = ntohl(lease); 176 if ((lease != DHCP_PERM) && 177 (orig_time + lease) <= current_time) 178 goto failure; 179 } 180 181 relativize_time(plp->opts[CD_T1_TIME], orig_time, current_time); 182 relativize_time(plp->opts[CD_T2_TIME], orig_time, current_time); 183 relativize_time(plp->opts[CD_LEASE_TIME], orig_time, 184 current_time); 185 } 186 187 (void) close(fd); 188 return (pcnt); 189 190 failure: 191 free(pkt); 192 free(plp); 193 while (pcnt-- > 0) { 194 free(plpp[pcnt]->pkt); 195 free(plpp[pcnt]); 196 } 197 (void) close(fd); 198 return (-1); 199 } 200 201 /* 202 * write_hostconf(): writes the contents of a PKT_LIST into an <if>.dhc file 203 * 204 * input: const char *: the interface name 205 * PKT_LIST **: a list of pointers to PKT_LIST to write 206 * int: length of the list of PKT_LIST pointers 207 * time_t: a starting time to treat the relative lease times 208 * in the first packet as relative to 209 * output: int: 0 if the file is written successfully, -1 otherwise 210 * (errno is set) 211 */ 212 213 int 214 write_hostconf( 215 const char *ifname, 216 PKT_LIST *pl[], 217 uint_t pllen, 218 time_t relative_to) 219 { 220 int fd; 221 struct iovec iov[IOV_MAX]; 222 int retval; 223 uint32_t magic = DHCP_HOSTCONF_MAGIC; 224 ssize_t explen = 0; /* Expected length of write */ 225 int i, iovlen = 0; 226 227 fd = open(ifname_to_hostconf(ifname), O_WRONLY|O_CREAT|O_TRUNC, 0600); 228 if (fd == -1) 229 return (-1); 230 231 /* 232 * first write our magic number, then the relative time of the 233 * leases, then for each packet we write the length of the packet 234 * followed by the packet. we will then use the relative time in 235 * read_hostconf() to recalculate the lease times for the first packet. 236 */ 237 238 iov[iovlen].iov_base = (caddr_t)&magic; 239 explen += iov[iovlen++].iov_len = sizeof (magic); 240 iov[iovlen].iov_base = (caddr_t)&relative_to; 241 explen += iov[iovlen++].iov_len = sizeof (relative_to); 242 for (i = 0; i < pllen && iovlen < (IOV_MAX - 1); i++) { 243 iov[iovlen].iov_base = (caddr_t)&pl[i]->len; 244 explen += iov[iovlen++].iov_len = sizeof (pl[i]->len); 245 iov[iovlen].iov_base = (caddr_t)pl[i]->pkt; 246 explen += iov[iovlen++].iov_len = pl[i]->len; 247 } 248 249 retval = writev(fd, iov, iovlen); 250 251 (void) close(fd); 252 253 if (retval != explen) 254 return (-1); 255 256 return (0); 257 } 258 259 /* 260 * relativize_time(): re-relativizes a time in a DHCP option 261 * 262 * input: DHCP_OPT *: the DHCP option parameter to convert 263 * time_t: the time the leases in the packet are currently relative to 264 * time_t: the current time which leases will become relative to 265 * output: void 266 */ 267 268 static void 269 relativize_time(DHCP_OPT *option, time_t orig_time, time_t current_time) 270 { 271 uint32_t pkt_time; 272 time_t time_diff = current_time - orig_time; 273 274 if (option == NULL || option->len != sizeof (lease_t)) 275 return; 276 277 (void) memcpy(&pkt_time, option->value, option->len); 278 if (ntohl(pkt_time) != DHCP_PERM) 279 pkt_time = htonl(ntohl(pkt_time) - time_diff); 280 281 (void) memcpy(option->value, &pkt_time, option->len); 282 } 283