1 0 stevel /* 2 0 stevel * CDDL HEADER START 3 0 stevel * 4 0 stevel * The contents of this file are subject to the terms of the 5 2157 dh155122 * Common Development and Distribution License (the "License"). 6 2157 dh155122 * You may not use this file except in compliance with the License. 7 0 stevel * 8 0 stevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 0 stevel * or http://www.opensolaris.org/os/licensing. 10 0 stevel * See the License for the specific language governing permissions 11 0 stevel * and limitations under the License. 12 0 stevel * 13 0 stevel * When distributing Covered Code, include this CDDL HEADER in each 14 0 stevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 0 stevel * If applicable, add the following below this CDDL HEADER, with the 16 0 stevel * fields enclosed by brackets "[]" replaced with your own identifying 17 0 stevel * information: Portions Copyright [yyyy] [name of copyright owner] 18 0 stevel * 19 0 stevel * CDDL HEADER END 20 0 stevel */ 21 0 stevel /* 22 8485 Peter * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 0 stevel * Use is subject to license terms. 24 0 stevel */ 25 0 stevel 26 0 stevel #include <sys/types.h> 27 0 stevel #include <sys/socket.h> 28 0 stevel #include <net/if.h> 29 0 stevel #include <stdlib.h> 30 0 stevel #include <sys/sockio.h> 31 0 stevel #include <netinet/in.h> 32 0 stevel #include <netinet/dhcp.h> 33 0 stevel #include <string.h> 34 0 stevel #include <unistd.h> 35 3431 carlsonj #include <search.h> 36 3431 carlsonj #include <libdevinfo.h> 37 4456 ss150715 #include <libdlpi.h> 38 0 stevel #include <netinet/if_ether.h> 39 3431 carlsonj #include <arpa/inet.h> 40 0 stevel #include <dhcpmsg.h> 41 0 stevel 42 3431 carlsonj #include "agent.h" 43 0 stevel #include "interface.h" 44 0 stevel #include "util.h" 45 0 stevel #include "packet.h" 46 0 stevel #include "states.h" 47 3431 carlsonj 48 3431 carlsonj dhcp_pif_t *v4root; 49 3431 carlsonj dhcp_pif_t *v6root; 50 3431 carlsonj 51 3431 carlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu; 52 0 stevel 53 0 stevel /* 54 3431 carlsonj * Interface flags to watch: things that should be under our direct control. 55 3431 carlsonj */ 56 4823 seb #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \ 57 4823 seb IFF_TEMPORARY) 58 3431 carlsonj 59 3431 carlsonj static void clear_lif_dhcp(dhcp_lif_t *); 60 3431 carlsonj 61 3431 carlsonj /* 62 3431 carlsonj * insert_pif(): creates a new physical interface structure and chains it on 63 3431 carlsonj * the list. Initializes state that remains consistent across 64 3431 carlsonj * all use of the physical interface entry. 65 0 stevel * 66 3431 carlsonj * input: const char *: the name of the physical interface 67 3431 carlsonj * boolean_t: if B_TRUE, this is DHCPv6 68 3431 carlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 69 3431 carlsonj * error code with the reason why 70 3431 carlsonj * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure 71 0 stevel */ 72 0 stevel 73 3431 carlsonj dhcp_pif_t * 74 3431 carlsonj insert_pif(const char *pname, boolean_t isv6, int *error) 75 3431 carlsonj { 76 3431 carlsonj dhcp_pif_t *pif; 77 3431 carlsonj struct lifreq lifr; 78 8485 Peter lifgroupinfo_t lifgr; 79 5381 meem dlpi_handle_t dh = NULL; 80 5978 meem int fd = isv6 ? v6_sock_fd : v4_sock_fd; 81 0 stevel 82 3431 carlsonj if ((pif = calloc(1, sizeof (*pif))) == NULL) { 83 3431 carlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for " 84 3431 carlsonj "%s", pname); 85 0 stevel *error = DHCP_IPC_E_MEMORY; 86 0 stevel return (NULL); 87 0 stevel } 88 0 stevel 89 3431 carlsonj pif->pif_isv6 = isv6; 90 3431 carlsonj pif->pif_hold_count = 1; 91 3431 carlsonj pif->pif_running = B_TRUE; 92 0 stevel 93 3431 carlsonj if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) { 94 3431 carlsonj dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long", 95 3431 carlsonj pname); 96 0 stevel *error = DHCP_IPC_E_INVIF; 97 0 stevel goto failure; 98 0 stevel } 99 0 stevel 100 5978 meem /* 101 5978 meem * This is a bit gross, but IP has a confused interface. We must 102 5978 meem * assume that the zeroth LIF is plumbed, and must query there to get 103 5978 meem * the interface index number. 104 5978 meem */ 105 5978 meem (void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ); 106 5978 meem 107 5978 meem if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 108 5978 meem *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 109 5978 meem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname); 110 5978 meem goto failure; 111 5978 meem } 112 5978 meem pif->pif_index = lifr.lifr_index; 113 11076 Cathy 114 11076 Cathy /* 115 11076 Cathy * Check if this is a VRRP interface. If yes, its IP addresses (the 116 11076 Cathy * VRRP virtual addresses) cannot be configured using DHCP. 117 11076 Cathy */ 118 11076 Cathy if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 119 11076 Cathy *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 120 11076 Cathy dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFFLAGS for %s", pname); 121 11076 Cathy goto failure; 122 11076 Cathy } 123 11076 Cathy 124 11076 Cathy if (lifr.lifr_flags & IFF_VRRP) { 125 11076 Cathy *error = DHCP_IPC_E_INVIF; 126 11076 Cathy dhcpmsg(MSG_ERROR, "insert_pif: VRRP virtual addresses over %s " 127 11076 Cathy "cannot be configured using DHCP", pname); 128 11076 Cathy goto failure; 129 11076 Cathy } 130 5978 meem 131 5978 meem if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) { 132 5978 meem *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 133 5978 meem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname); 134 5978 meem goto failure; 135 5978 meem } 136 5978 meem pif->pif_max = lifr.lifr_mtu; 137 5978 meem 138 5978 meem if (pif->pif_max < DHCP_DEF_MAX_SIZE) { 139 5978 meem dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to " 140 5978 meem "support DHCP (%u < %u)", pname, pif->pif_max, 141 5978 meem DHCP_DEF_MAX_SIZE); 142 5978 meem *error = DHCP_IPC_E_INVIF; 143 5978 meem goto failure; 144 5978 meem } 145 5978 meem 146 5978 meem /* 147 8485 Peter * Check if the pif is in an IPMP group. Interfaces using IPMP don't 148 8485 Peter * have dedicated hardware addresses, and get their hardware type from 149 8485 Peter * the SIOCGLIFGROUPINFO ioctl rather than DLPI. 150 5978 meem */ 151 8485 Peter if (ioctl(fd, SIOCGLIFGROUPNAME, &lifr) == -1) { 152 8485 Peter *error = DHCP_IPC_E_INT; 153 8485 Peter dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPNAME for %s", pname); 154 8485 Peter goto failure; 155 8485 Peter } 156 8485 Peter 157 8485 Peter if (lifr.lifr_groupname[0] != '\0') { 158 8485 Peter (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, 159 8485 Peter LIFGRNAMSIZ); 160 8485 Peter if (ioctl(fd, SIOCGLIFGROUPINFO, &lifgr) == -1) { 161 8485 Peter *error = DHCP_IPC_E_INT; 162 8485 Peter dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPINFO for %s", 163 8485 Peter lifgr.gi_grname); 164 8485 Peter goto failure; 165 8485 Peter } 166 8485 Peter 167 8485 Peter pif->pif_hwtype = dlpi_arptype(lifgr.gi_mactype); 168 8485 Peter pif->pif_under_ipmp = (strcmp(pname, lifgr.gi_grifname) != 0); 169 8485 Peter (void) strlcpy(pif->pif_grifname, lifgr.gi_grifname, LIFNAMSIZ); 170 8485 Peter 171 8485 Peter /* 172 8485 Peter * For IPMP underlying interfaces, stash the interface index 173 8485 Peter * of the IPMP meta-interface; we'll use it to send/receive 174 8485 Peter * traffic. This is both necessary (since IP_BOUND_IF for 175 8485 Peter * non-unicast traffic won't work on underlying interfaces) 176 8485 Peter * and preferred (since a test address lease will be able to 177 8485 Peter * be maintained as long as another interface in the group is 178 8485 Peter * still functioning). 179 8485 Peter */ 180 8485 Peter if (pif->pif_under_ipmp) { 181 8485 Peter (void) strlcpy(lifr.lifr_name, pif->pif_grifname, 182 8485 Peter LIFNAMSIZ); 183 8485 Peter 184 8485 Peter if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 185 8485 Peter *error = DHCP_IPC_E_INT; 186 8485 Peter dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX " 187 8485 Peter "for %s", lifr.lifr_name); 188 8485 Peter goto failure; 189 8485 Peter } 190 8485 Peter pif->pif_grindex = lifr.lifr_index; 191 8485 Peter } 192 8485 Peter } 193 8485 Peter 194 8485 Peter /* 195 8485 Peter * For IPv4, if the hardware type is still unknown, use DLPI to 196 8485 Peter * determine it, the hardware address, and hardware address length. 197 8485 Peter */ 198 8485 Peter if (!isv6 && pif->pif_hwtype == 0) { 199 8485 Peter int rc; 200 8485 Peter dlpi_info_t dlinfo; 201 0 stevel 202 4456 ss150715 if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) { 203 4456 ss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s", 204 4456 ss150715 dlpi_strerror(rc)); 205 4456 ss150715 *error = DHCP_IPC_E_INVIF; 206 4456 ss150715 goto failure; 207 4456 ss150715 } 208 4456 ss150715 209 4456 ss150715 if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) { 210 4456 ss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s", 211 4456 ss150715 dlpi_strerror(rc)); 212 3431 carlsonj *error = DHCP_IPC_E_INVIF; 213 3431 carlsonj goto failure; 214 3431 carlsonj } 215 3431 carlsonj 216 5978 meem if ((rc = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) { 217 4456 ss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s", 218 4456 ss150715 dlpi_strerror(rc)); 219 4456 ss150715 *error = DHCP_IPC_E_INVIF; 220 4456 ss150715 goto failure; 221 4456 ss150715 } 222 4456 ss150715 223 4456 ss150715 pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype); 224 4456 ss150715 pif->pif_hwlen = dlinfo.di_physaddrlen; 225 3431 carlsonj 226 5381 meem dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d", 227 5381 meem pname, pif->pif_hwtype, pif->pif_hwlen); 228 3431 carlsonj 229 3431 carlsonj if (pif->pif_hwlen > 0) { 230 3431 carlsonj pif->pif_hwaddr = malloc(pif->pif_hwlen); 231 3431 carlsonj if (pif->pif_hwaddr == NULL) { 232 3431 carlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate " 233 3431 carlsonj "pif_hwaddr for %s", pname); 234 3431 carlsonj *error = DHCP_IPC_E_MEMORY; 235 3431 carlsonj goto failure; 236 3431 carlsonj } 237 5381 meem (void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr, 238 5381 meem pif->pif_hwlen); 239 3431 carlsonj } 240 3431 carlsonj 241 5381 meem dlpi_close(dh); 242 5381 meem dh = NULL; 243 3431 carlsonj } 244 3431 carlsonj 245 3431 carlsonj insque(pif, isv6 ? &v6root : &v4root); 246 3431 carlsonj 247 3431 carlsonj return (pif); 248 3431 carlsonj failure: 249 5381 meem if (dh != NULL) 250 5381 meem dlpi_close(dh); 251 3431 carlsonj release_pif(pif); 252 3431 carlsonj return (NULL); 253 3431 carlsonj } 254 3431 carlsonj 255 3431 carlsonj /* 256 3431 carlsonj * hold_pif(): acquire a hold on a physical interface structure. 257 3431 carlsonj * 258 3431 carlsonj * input: dhcp_pif_t *: a pointer to the PIF structure 259 3431 carlsonj * output: none 260 3431 carlsonj */ 261 3431 carlsonj 262 3431 carlsonj void 263 3431 carlsonj hold_pif(dhcp_pif_t *pif) 264 3431 carlsonj { 265 3431 carlsonj pif->pif_hold_count++; 266 3431 carlsonj dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name, 267 3431 carlsonj pif->pif_hold_count); 268 3431 carlsonj } 269 3431 carlsonj 270 3431 carlsonj /* 271 3431 carlsonj * release_pif(): release a hold on a physical interface structure; will 272 3431 carlsonj * destroy the structure on the last hold removed. 273 3431 carlsonj * 274 3431 carlsonj * input: dhcp_pif_t *: a pointer to the PIF structure 275 3431 carlsonj * output: none 276 3431 carlsonj */ 277 3431 carlsonj 278 3431 carlsonj void 279 3431 carlsonj release_pif(dhcp_pif_t *pif) 280 3431 carlsonj { 281 3431 carlsonj if (pif->pif_hold_count == 0) { 282 3431 carlsonj dhcpmsg(MSG_CRIT, "release_pif: extraneous release"); 283 3431 carlsonj return; 284 3431 carlsonj } 285 3431 carlsonj 286 3431 carlsonj if (--pif->pif_hold_count == 0) { 287 3431 carlsonj dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s", 288 3431 carlsonj pif->pif_name); 289 3431 carlsonj 290 3431 carlsonj remque(pif); 291 3431 carlsonj free(pif->pif_hwaddr); 292 3431 carlsonj free(pif); 293 3431 carlsonj } else { 294 3431 carlsonj dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u", 295 3431 carlsonj pif->pif_name, pif->pif_hold_count); 296 3431 carlsonj } 297 3431 carlsonj } 298 3431 carlsonj 299 3431 carlsonj /* 300 3431 carlsonj * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and 301 3431 carlsonj * previous PIF pointer (or NULL for list start). 302 3431 carlsonj * Caller is expected to iterate through all 303 3431 carlsonj * potential matches to find interface of interest. 304 3431 carlsonj * 305 3431 carlsonj * input: uint16_t: the interface index (truncated) 306 3431 carlsonj * dhcp_pif_t *: the previous PIF, or NULL for list start 307 3431 carlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 308 3431 carlsonj * output: dhcp_pif_t *: the next matching PIF, or NULL if not found 309 3431 carlsonj * note: This operates using the 'truncated' (16-bit) ifindex as seen by 310 3431 carlsonj * routing socket clients. The value stored in pif_index is the 311 3431 carlsonj * 32-bit ifindex from the ioctl interface. 312 3431 carlsonj */ 313 3431 carlsonj 314 3431 carlsonj dhcp_pif_t * 315 3431 carlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6) 316 3431 carlsonj { 317 3431 carlsonj if (pif == NULL) 318 3431 carlsonj pif = isv6 ? v6root : v4root; 319 3431 carlsonj else 320 3431 carlsonj pif = pif->pif_next; 321 3431 carlsonj 322 3431 carlsonj for (; pif != NULL; pif = pif->pif_next) { 323 3431 carlsonj if ((pif->pif_index & 0xffff) == ifindex) 324 3431 carlsonj break; 325 3431 carlsonj } 326 3431 carlsonj 327 3431 carlsonj return (pif); 328 3431 carlsonj } 329 3431 carlsonj 330 3431 carlsonj /* 331 3431 carlsonj * lookup_pif_by_name(): Looks up a physical interface entry given a name. 332 3431 carlsonj * 333 3431 carlsonj * input: const char *: the physical interface name 334 3431 carlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 335 3431 carlsonj * output: dhcp_pif_t *: the matching PIF, or NULL if not found 336 3431 carlsonj */ 337 3431 carlsonj 338 3431 carlsonj dhcp_pif_t * 339 3431 carlsonj lookup_pif_by_name(const char *pname, boolean_t isv6) 340 3431 carlsonj { 341 3431 carlsonj dhcp_pif_t *pif; 342 3431 carlsonj 343 3431 carlsonj pif = isv6 ? v6root : v4root; 344 3431 carlsonj 345 3431 carlsonj for (; pif != NULL; pif = pif->pif_next) { 346 3431 carlsonj if (strcmp(pif->pif_name, pname) == 0) 347 3431 carlsonj break; 348 3431 carlsonj } 349 3431 carlsonj 350 3431 carlsonj return (pif); 351 3431 carlsonj } 352 3431 carlsonj 353 3431 carlsonj /* 354 3431 carlsonj * pif_status(): update the physical interface up/down status. 355 3431 carlsonj * 356 5381 meem * input: dhcp_pif_t *: the physical interface to be updated 357 3431 carlsonj * boolean_t: B_TRUE if the interface is going up 358 3431 carlsonj * output: none 359 3431 carlsonj */ 360 3431 carlsonj 361 3431 carlsonj void 362 3431 carlsonj pif_status(dhcp_pif_t *pif, boolean_t isup) 363 3431 carlsonj { 364 3431 carlsonj dhcp_lif_t *lif; 365 3431 carlsonj dhcp_smach_t *dsmp; 366 3431 carlsonj 367 3431 carlsonj pif->pif_running = isup; 368 4516 carlsonj dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name, 369 3431 carlsonj isup ? "come back up" : "gone down"); 370 3431 carlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 371 3431 carlsonj for (dsmp = lif->lif_smachs; dsmp != NULL; 372 3431 carlsonj dsmp = dsmp->dsm_next) { 373 3431 carlsonj if (isup) 374 3431 carlsonj refresh_smach(dsmp); 375 3431 carlsonj else 376 3431 carlsonj remove_default_routes(dsmp); 377 3431 carlsonj } 378 3431 carlsonj } 379 3431 carlsonj } 380 3431 carlsonj 381 3431 carlsonj /* Helper for insert_lif: extract addresses as defined */ 382 3431 carlsonj #define ASSIGN_ADDR(v4, v6, lf) \ 383 3431 carlsonj if (pif->pif_isv6) { \ 384 3431 carlsonj lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \ 385 3431 carlsonj } else { \ 386 3431 carlsonj lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \ 387 3431 carlsonj } 388 3431 carlsonj 389 3431 carlsonj /* 390 3431 carlsonj * insert_lif(): Creates a new logical interface structure and chains it on 391 3431 carlsonj * the list for a given physical interface. Initializes state 392 3431 carlsonj * that remains consistent across all use of the logical 393 3431 carlsonj * interface entry. Caller's PIF hold is transferred to the 394 3431 carlsonj * LIF on success, and is dropped on failure. 395 3431 carlsonj * 396 3431 carlsonj * input: dhcp_pif_t *: pointer to the physical interface for this LIF 397 3431 carlsonj * const char *: the name of the logical interface 398 3431 carlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 399 3431 carlsonj * error code with the reason why 400 3431 carlsonj * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure 401 3431 carlsonj */ 402 3431 carlsonj 403 3431 carlsonj dhcp_lif_t * 404 3431 carlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error) 405 3431 carlsonj { 406 3431 carlsonj dhcp_lif_t *lif; 407 3431 carlsonj int fd; 408 3431 carlsonj struct lifreq lifr; 409 3431 carlsonj 410 3431 carlsonj if ((lif = calloc(1, sizeof (*lif))) == NULL) { 411 3431 carlsonj dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for " 412 3431 carlsonj "%s", lname); 413 3431 carlsonj *error = DHCP_IPC_E_MEMORY; 414 3431 carlsonj return (NULL); 415 3431 carlsonj } 416 3431 carlsonj 417 3431 carlsonj lif->lif_sock_ip_fd = -1; 418 5381 meem lif->lif_packet_id = -1; 419 3431 carlsonj lif->lif_iaid_id = -1; 420 3431 carlsonj lif->lif_hold_count = 1; 421 3431 carlsonj lif->lif_pif = pif; 422 3431 carlsonj lif->lif_removed = B_TRUE; 423 3431 carlsonj init_timer(&lif->lif_preferred, 0); 424 3431 carlsonj init_timer(&lif->lif_expire, 0); 425 3431 carlsonj 426 3431 carlsonj if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) { 427 3431 carlsonj dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long", 428 3431 carlsonj lname); 429 0 stevel *error = DHCP_IPC_E_INVIF; 430 0 stevel goto failure; 431 0 stevel } 432 0 stevel 433 3431 carlsonj (void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ); 434 0 stevel 435 3431 carlsonj fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 436 0 stevel 437 3431 carlsonj if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) 438 3431 carlsonj lif->lif_max = 1024; 439 3431 carlsonj else 440 3431 carlsonj lif->lif_max = lifr.lifr_mtu; 441 0 stevel 442 3431 carlsonj if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) { 443 2546 carlsonj if (errno == ENXIO) 444 2546 carlsonj *error = DHCP_IPC_E_INVIF; 445 2546 carlsonj else 446 2546 carlsonj *error = DHCP_IPC_E_INT; 447 3431 carlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname); 448 2546 carlsonj goto failure; 449 2546 carlsonj } 450 3431 carlsonj ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr); 451 0 stevel 452 3431 carlsonj if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 453 0 stevel if (errno == ENXIO) 454 0 stevel *error = DHCP_IPC_E_INVIF; 455 0 stevel else 456 0 stevel *error = DHCP_IPC_E_INT; 457 3431 carlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname); 458 0 stevel goto failure; 459 3431 carlsonj } 460 3431 carlsonj ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr); 461 3431 carlsonj 462 3431 carlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 463 3431 carlsonj *error = DHCP_IPC_E_INT; 464 3431 carlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname); 465 3431 carlsonj goto failure; 466 3431 carlsonj } 467 3431 carlsonj lif->lif_flags = lifr.lifr_flags; 468 3431 carlsonj 469 3431 carlsonj /* 470 3431 carlsonj * If we've just detected the interface going up or down, then signal 471 3431 carlsonj * an appropriate action. There may be other state machines here. 472 3431 carlsonj */ 473 3431 carlsonj if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) { 474 3431 carlsonj pif_status(pif, B_TRUE); 475 3431 carlsonj } else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) { 476 3431 carlsonj pif_status(pif, B_FALSE); 477 3431 carlsonj } 478 3431 carlsonj 479 3431 carlsonj if (lifr.lifr_flags & IFF_POINTOPOINT) { 480 3431 carlsonj if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) { 481 3431 carlsonj *error = DHCP_IPC_E_INT; 482 3431 carlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s", 483 3431 carlsonj lname); 484 3431 carlsonj goto failure; 485 3431 carlsonj } 486 3431 carlsonj ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr); 487 3431 carlsonj } else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) { 488 3431 carlsonj if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) { 489 3431 carlsonj *error = DHCP_IPC_E_INT; 490 3431 carlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s", 491 3431 carlsonj lname); 492 3431 carlsonj goto failure; 493 3431 carlsonj } 494 3431 carlsonj lif->lif_broadcast = 495 3431 carlsonj ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr. 496 3431 carlsonj s_addr; 497 3431 carlsonj } 498 3431 carlsonj 499 3431 carlsonj if (pif->pif_isv6) 500 3431 carlsonj cached_v6_max_mtu = 0; 501 3431 carlsonj else 502 3431 carlsonj cached_v4_max_mtu = 0; 503 3431 carlsonj 504 3431 carlsonj lif->lif_removed = B_FALSE; 505 3431 carlsonj insque(lif, &pif->pif_lifs); 506 3431 carlsonj 507 3431 carlsonj return (lif); 508 3431 carlsonj 509 3431 carlsonj failure: 510 3431 carlsonj release_lif(lif); 511 3431 carlsonj return (NULL); 512 3431 carlsonj } 513 3431 carlsonj 514 3431 carlsonj /* 515 3431 carlsonj * hold_lif(): acquire a hold on a logical interface structure. 516 3431 carlsonj * 517 3431 carlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 518 3431 carlsonj * output: none 519 3431 carlsonj */ 520 3431 carlsonj 521 3431 carlsonj void 522 3431 carlsonj hold_lif(dhcp_lif_t *lif) 523 3431 carlsonj { 524 3431 carlsonj lif->lif_hold_count++; 525 3431 carlsonj dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name, 526 3431 carlsonj lif->lif_hold_count); 527 3431 carlsonj } 528 3431 carlsonj 529 3431 carlsonj /* 530 3431 carlsonj * release_lif(): release a hold on a logical interface structure; will 531 3431 carlsonj * destroy the structure on the last hold removed. 532 3431 carlsonj * 533 3431 carlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 534 3431 carlsonj * output: none 535 3431 carlsonj */ 536 3431 carlsonj 537 3431 carlsonj void 538 3431 carlsonj release_lif(dhcp_lif_t *lif) 539 3431 carlsonj { 540 3431 carlsonj if (lif->lif_hold_count == 0) { 541 3431 carlsonj dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s", 542 3431 carlsonj lif->lif_name); 543 3431 carlsonj return; 544 3431 carlsonj } 545 3431 carlsonj 546 3431 carlsonj if (lif->lif_hold_count == 1 && !lif->lif_removed) { 547 3431 carlsonj unplumb_lif(lif); 548 3431 carlsonj return; 549 3431 carlsonj } 550 3431 carlsonj 551 3431 carlsonj if (--lif->lif_hold_count == 0) { 552 3431 carlsonj dhcp_pif_t *pif; 553 3431 carlsonj 554 3431 carlsonj dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s", 555 3431 carlsonj lif->lif_name); 556 3431 carlsonj 557 3431 carlsonj if (lif->lif_lease != NULL) 558 3431 carlsonj dhcpmsg(MSG_CRIT, 559 3431 carlsonj "release_lif: still holding lease at last hold!"); 560 3431 carlsonj close_ip_lif(lif); 561 3431 carlsonj pif = lif->lif_pif; 562 3431 carlsonj if (pif->pif_isv6) 563 3431 carlsonj cached_v6_max_mtu = 0; 564 3431 carlsonj else 565 3431 carlsonj cached_v4_max_mtu = 0; 566 3431 carlsonj release_pif(pif); 567 3431 carlsonj free(lif); 568 3431 carlsonj } else { 569 3431 carlsonj dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u", 570 3431 carlsonj lif->lif_name, lif->lif_hold_count); 571 3431 carlsonj } 572 3431 carlsonj } 573 3431 carlsonj 574 3431 carlsonj /* 575 3431 carlsonj * remove_lif(): remove a logical interface from its PIF and lease (if any) and 576 3431 carlsonj * the lease's hold on the LIF. Assumes that we did not plumb 577 3431 carlsonj * the interface. 578 3431 carlsonj * 579 3431 carlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 580 3431 carlsonj * output: none 581 3431 carlsonj */ 582 3431 carlsonj 583 3431 carlsonj void 584 3431 carlsonj remove_lif(dhcp_lif_t *lif) 585 3431 carlsonj { 586 3431 carlsonj if (lif->lif_plumbed) { 587 3431 carlsonj dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s", 588 3431 carlsonj lif->lif_name); 589 3431 carlsonj return; 590 3431 carlsonj } 591 3431 carlsonj if (lif->lif_removed) { 592 3431 carlsonj dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s", 593 3431 carlsonj lif->lif_name); 594 3431 carlsonj } else { 595 3431 carlsonj dhcp_lif_t *lifnext; 596 3431 carlsonj dhcp_lease_t *dlp; 597 3431 carlsonj 598 3431 carlsonj dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name); 599 3431 carlsonj lif->lif_removed = B_TRUE; 600 3431 carlsonj lifnext = lif->lif_next; 601 3431 carlsonj clear_lif_dhcp(lif); 602 3431 carlsonj cancel_lif_timers(lif); 603 3431 carlsonj if (lif->lif_iaid_id != -1 && 604 3431 carlsonj iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) { 605 3431 carlsonj lif->lif_iaid_id = -1; 606 3431 carlsonj release_lif(lif); 607 3431 carlsonj } 608 3431 carlsonj 609 3431 carlsonj /* Remove from PIF list */ 610 3431 carlsonj remque(lif); 611 3431 carlsonj 612 3431 carlsonj /* If we were part of a lease, then remove ourselves */ 613 3431 carlsonj if ((dlp = lif->lif_lease) != NULL) { 614 3431 carlsonj if (--dlp->dl_nlifs == 0) 615 3431 carlsonj dlp->dl_lifs = NULL; 616 3431 carlsonj else if (dlp->dl_lifs == lif) 617 3431 carlsonj dlp->dl_lifs = lifnext; 618 3431 carlsonj if (lif->lif_declined != NULL) { 619 3431 carlsonj dlp->dl_smach->dsm_lif_down--; 620 3431 carlsonj lif->lif_declined = NULL; 621 3431 carlsonj } 622 9629 james if (lif->lif_dad_wait) { 623 9629 james lif->lif_dad_wait = _B_FALSE; 624 9629 james dlp->dl_smach->dsm_lif_wait--; 625 9629 james } 626 3431 carlsonj lif->lif_lease = NULL; 627 3431 carlsonj release_lif(lif); 628 3431 carlsonj } 629 3431 carlsonj } 630 3431 carlsonj } 631 3431 carlsonj 632 3431 carlsonj /* 633 3431 carlsonj * lookup_lif_by_name(): Looks up a logical interface entry given a name and 634 3431 carlsonj * a physical interface. 635 3431 carlsonj * 636 3431 carlsonj * input: const char *: the logical interface name 637 3431 carlsonj * const dhcp_pif_t *: the physical interface 638 3431 carlsonj * output: dhcp_lif_t *: the matching LIF, or NULL if not found 639 3431 carlsonj */ 640 3431 carlsonj 641 3431 carlsonj dhcp_lif_t * 642 3431 carlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif) 643 3431 carlsonj { 644 3431 carlsonj dhcp_lif_t *lif; 645 3431 carlsonj 646 3431 carlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 647 3431 carlsonj if (strcmp(lif->lif_name, lname) == 0) 648 3431 carlsonj break; 649 3431 carlsonj } 650 3431 carlsonj 651 3431 carlsonj return (lif); 652 3431 carlsonj } 653 3431 carlsonj 654 3431 carlsonj /* 655 3431 carlsonj * checkaddr(): checks if the given address is still set on the given LIF 656 3431 carlsonj * 657 3431 carlsonj * input: const dhcp_lif_t *: the LIF to check 658 3431 carlsonj * int: the address to look up on the interface (ioctl) 659 3431 carlsonj * const in6_addr_t *: the address to compare to 660 3431 carlsonj * const char *: name of the address for logging purposes 661 3431 carlsonj * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not 662 3431 carlsonj */ 663 3431 carlsonj 664 3431 carlsonj static boolean_t 665 3431 carlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr, 666 3431 carlsonj const char *aname) 667 3431 carlsonj { 668 3431 carlsonj boolean_t isv6; 669 3431 carlsonj int fd; 670 3431 carlsonj struct lifreq lifr; 671 5381 meem char abuf1[INET6_ADDRSTRLEN]; 672 5381 meem char abuf2[INET6_ADDRSTRLEN]; 673 3431 carlsonj 674 3431 carlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 675 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 676 3431 carlsonj 677 3431 carlsonj isv6 = lif->lif_pif->pif_isv6; 678 3431 carlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 679 3431 carlsonj 680 3431 carlsonj if (ioctl(fd, ioccmd, &lifr) == -1) { 681 3431 carlsonj if (errno == ENXIO) { 682 3431 carlsonj dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone", 683 3431 carlsonj lif->lif_name); 684 3431 carlsonj return (B_FALSE); 685 3431 carlsonj } 686 3431 carlsonj dhcpmsg(MSG_DEBUG, 687 3431 carlsonj "checkaddr: ignoring ioctl error on %s %x: %s", 688 3431 carlsonj lif->lif_name, ioccmd, strerror(errno)); 689 3431 carlsonj } else if (isv6) { 690 3431 carlsonj struct sockaddr_in6 *sin6 = 691 3431 carlsonj (struct sockaddr_in6 *)&lifr.lifr_addr; 692 3431 carlsonj 693 3431 carlsonj if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) { 694 3431 carlsonj dhcpmsg(MSG_WARNING, 695 5381 meem "checkaddr: expected %s %s on %s, have %s", aname, 696 5381 meem inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)), 697 5381 meem lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr, 698 5381 meem abuf2, sizeof (abuf2))); 699 3431 carlsonj return (B_FALSE); 700 3431 carlsonj } 701 3431 carlsonj } else { 702 3431 carlsonj struct sockaddr_in *sinp = 703 3431 carlsonj (struct sockaddr_in *)&lifr.lifr_addr; 704 3431 carlsonj ipaddr_t v4addr; 705 3431 carlsonj 706 3431 carlsonj IN6_V4MAPPED_TO_IPADDR(addr, v4addr); 707 3431 carlsonj if (sinp->sin_addr.s_addr != v4addr) { 708 3431 carlsonj dhcpmsg(MSG_WARNING, 709 5381 meem "checkaddr: expected %s %s on %s, have %s", aname, 710 5381 meem inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)), 711 5381 meem lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr, 712 5381 meem abuf2, sizeof (abuf2))); 713 3431 carlsonj return (B_FALSE); 714 3431 carlsonj } 715 3431 carlsonj } 716 3431 carlsonj return (B_TRUE); 717 3431 carlsonj } 718 3431 carlsonj 719 3431 carlsonj /* 720 3431 carlsonj * verify_lif(): verifies than a LIF is still valid (i.e., has not been 721 3431 carlsonj * explicitly or implicitly dropped or released) 722 3431 carlsonj * 723 3431 carlsonj * input: const dhcp_lif_t *: the LIF to verify 724 3431 carlsonj * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise 725 3431 carlsonj */ 726 3431 carlsonj 727 3431 carlsonj boolean_t 728 3431 carlsonj verify_lif(const dhcp_lif_t *lif) 729 3431 carlsonj { 730 3431 carlsonj boolean_t isv6; 731 3431 carlsonj int fd; 732 3431 carlsonj struct lifreq lifr; 733 8485 Peter dhcp_pif_t *pif = lif->lif_pif; 734 3431 carlsonj 735 3431 carlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 736 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 737 3431 carlsonj 738 8485 Peter isv6 = pif->pif_isv6; 739 3431 carlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 740 3431 carlsonj 741 3431 carlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 742 4106 carlsonj if (errno != ENXIO) { 743 4106 carlsonj dhcpmsg(MSG_ERR, 744 4106 carlsonj "verify_lif: SIOCGLIFFLAGS failed on %s", 745 4106 carlsonj lif->lif_name); 746 4106 carlsonj } 747 3431 carlsonj return (B_FALSE); 748 3431 carlsonj } 749 3431 carlsonj 750 3431 carlsonj /* 751 3431 carlsonj * If important flags have changed, then abandon the interface. 752 3431 carlsonj */ 753 3431 carlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) { 754 3431 carlsonj dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: " 755 3431 carlsonj "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags, 756 3431 carlsonj lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) & 757 3431 carlsonj DHCP_IFF_WATCH); 758 3431 carlsonj return (B_FALSE); 759 3431 carlsonj } 760 3431 carlsonj 761 3431 carlsonj /* 762 8485 Peter * Check for delete and recreate. 763 3431 carlsonj */ 764 8485 Peter if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 765 8485 Peter if (errno != ENXIO) { 766 8485 Peter dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed " 767 8485 Peter "on %s", lif->lif_name); 768 8485 Peter } 769 8485 Peter return (B_FALSE); 770 3431 carlsonj } 771 8485 Peter if (lifr.lifr_index != pif->pif_index) { 772 8485 Peter dhcpmsg(MSG_DEBUG, 773 8485 Peter "verify_lif: ifindex on %s changed: %u to %u", 774 8485 Peter lif->lif_name, pif->pif_index, lifr.lifr_index); 775 3431 carlsonj return (B_FALSE); 776 3431 carlsonj } 777 3431 carlsonj 778 8485 Peter if (pif->pif_under_ipmp) { 779 8485 Peter (void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ); 780 8485 Peter 781 8485 Peter if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 782 8485 Peter if (errno != ENXIO) { 783 8485 Peter dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX " 784 8485 Peter "failed on %s", lifr.lifr_name); 785 8485 Peter } 786 8485 Peter return (B_FALSE); 787 8485 Peter } 788 8485 Peter 789 8485 Peter if (lifr.lifr_index != pif->pif_grindex) { 790 8485 Peter dhcpmsg(MSG_DEBUG, "verify_lif: IPMP group ifindex " 791 8485 Peter "on %s changed: %u to %u", lifr.lifr_name, 792 8485 Peter pif->pif_grindex, lifr.lifr_index); 793 8485 Peter return (B_FALSE); 794 8485 Peter } 795 3431 carlsonj } 796 3431 carlsonj 797 3431 carlsonj /* 798 3431 carlsonj * If the IP address, netmask, or broadcast address have changed, or 799 3431 carlsonj * the interface has been unplumbed, then we act like there has been an 800 3431 carlsonj * implicit drop. (Note that the netmask is under DHCP control for 801 3431 carlsonj * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast 802 3431 carlsonj * addresses.) 803 3431 carlsonj */ 804 3431 carlsonj 805 3431 carlsonj if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address")) 806 3431 carlsonj return (B_FALSE); 807 3431 carlsonj 808 3431 carlsonj if (isv6) { 809 3431 carlsonj /* 810 3431 carlsonj * If it's not point-to-point, we're done. If it is, then 811 3431 carlsonj * check the peer's address as well. 812 3431 carlsonj */ 813 3431 carlsonj return (!(lif->lif_flags & IFF_POINTOPOINT) || 814 3431 carlsonj checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer, 815 3431 carlsonj "peer address")); 816 3431 carlsonj } else { 817 3431 carlsonj if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask, 818 3431 carlsonj "netmask")) 819 3431 carlsonj return (B_FALSE); 820 3431 carlsonj 821 3431 carlsonj return (checkaddr(lif, 822 3431 carlsonj (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR : 823 3431 carlsonj SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address")); 824 3431 carlsonj } 825 3431 carlsonj } 826 3431 carlsonj 827 3431 carlsonj /* 828 3431 carlsonj * canonize_lif(): puts the interface in a canonical (zeroed) form. This is 829 3431 carlsonj * used only on the "main" LIF for IPv4. All other interfaces 830 3431 carlsonj * are under dhcpagent control and are removed using 831 3431 carlsonj * unplumb_lif(). 832 3431 carlsonj * 833 3431 carlsonj * input: dhcp_lif_t *: the interface to canonize 834 5381 meem * boolean_t: only canonize lif if it's under DHCP control 835 3431 carlsonj * output: none 836 3431 carlsonj */ 837 3431 carlsonj 838 3431 carlsonj static void 839 5381 meem canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly) 840 3431 carlsonj { 841 3431 carlsonj boolean_t isv6; 842 3431 carlsonj int fd; 843 3431 carlsonj struct lifreq lifr; 844 3431 carlsonj 845 3431 carlsonj /* 846 3431 carlsonj * If there's nothing here, then don't touch the interface. This can 847 3431 carlsonj * happen when an already-canonized LIF is recanonized. 848 3431 carlsonj */ 849 3431 carlsonj if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr)) 850 3431 carlsonj return; 851 3431 carlsonj 852 3431 carlsonj isv6 = lif->lif_pif->pif_isv6; 853 3431 carlsonj dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s", 854 3431 carlsonj isv6 ? 6 : 4, lif->lif_name); 855 3431 carlsonj 856 3431 carlsonj lif->lif_v6addr = my_in6addr_any; 857 3431 carlsonj lif->lif_v6mask = my_in6addr_any; 858 3431 carlsonj lif->lif_v6peer = my_in6addr_any; 859 3431 carlsonj 860 3431 carlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 861 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 862 3431 carlsonj 863 3431 carlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 864 3431 carlsonj 865 3431 carlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 866 4106 carlsonj if (errno != ENXIO) { 867 4106 carlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s", 868 4106 carlsonj lif->lif_name); 869 4106 carlsonj } 870 3431 carlsonj return; 871 3431 carlsonj } 872 6637 meem lif->lif_flags = lifr.lifr_flags; 873 3431 carlsonj 874 5381 meem if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) { 875 3431 carlsonj dhcpmsg(MSG_INFO, 876 3431 carlsonj "canonize_lif: cannot clear %s; flags are %llx", 877 3431 carlsonj lif->lif_name, lifr.lifr_flags); 878 3431 carlsonj return; 879 3431 carlsonj } 880 3431 carlsonj 881 3431 carlsonj (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); 882 3431 carlsonj if (isv6) { 883 3431 carlsonj struct sockaddr_in6 *sin6 = 884 3431 carlsonj (struct sockaddr_in6 *)&lifr.lifr_addr; 885 3431 carlsonj 886 3431 carlsonj sin6->sin6_family = AF_INET6; 887 3431 carlsonj sin6->sin6_addr = my_in6addr_any; 888 3431 carlsonj } else { 889 3431 carlsonj struct sockaddr_in *sinv = 890 3431 carlsonj (struct sockaddr_in *)&lifr.lifr_addr; 891 3431 carlsonj 892 3431 carlsonj sinv->sin_family = AF_INET; 893 3431 carlsonj sinv->sin_addr.s_addr = htonl(INADDR_ANY); 894 3431 carlsonj } 895 3431 carlsonj 896 3431 carlsonj if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) { 897 3431 carlsonj dhcpmsg(MSG_ERR, 898 3431 carlsonj "canonize_lif: can't clear local address on %s", 899 3431 carlsonj lif->lif_name); 900 3431 carlsonj } 901 3431 carlsonj 902 9629 james /* Clearing the address means that we're no longer waiting on DAD */ 903 9629 james if (lif->lif_dad_wait) { 904 9629 james lif->lif_dad_wait = _B_FALSE; 905 9629 james lif->lif_lease->dl_smach->dsm_lif_wait--; 906 9629 james } 907 9629 james 908 3431 carlsonj if (lif->lif_flags & IFF_POINTOPOINT) { 909 3431 carlsonj if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) { 910 3431 carlsonj dhcpmsg(MSG_ERR, 911 3431 carlsonj "canonize_lif: can't clear remote address on %s", 912 3431 carlsonj lif->lif_name); 913 3431 carlsonj } 914 3431 carlsonj } else if (!isv6) { 915 3431 carlsonj if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) { 916 3431 carlsonj dhcpmsg(MSG_ERR, 917 3431 carlsonj "canonize_lif: can't clear broadcast address on %s", 918 3431 carlsonj lif->lif_name); 919 8403 Anurag } 920 8403 Anurag } 921 8403 Anurag 922 8403 Anurag /* 923 8403 Anurag * Clear the netmask last as it has to be refetched after clearing. 924 8403 Anurag * Netmask is under in.ndpd control with IPv6. 925 8403 Anurag */ 926 8403 Anurag if (!isv6) { 927 8403 Anurag /* Clear the netmask */ 928 8403 Anurag if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) { 929 8403 Anurag dhcpmsg(MSG_ERR, 930 8403 Anurag "canonize_lif: can't clear netmask on %s", 931 8403 Anurag lif->lif_name); 932 8403 Anurag } else { 933 8403 Anurag /* 934 8403 Anurag * When the netmask is cleared, the kernel actually sets 935 8403 Anurag * the netmask to 255.0.0.0. So, refetch that netmask. 936 8403 Anurag */ 937 8403 Anurag if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 938 8403 Anurag dhcpmsg(MSG_ERR, 939 8403 Anurag "canonize_lif: can't reload cleared " 940 8403 Anurag "netmask on %s", lif->lif_name); 941 8403 Anurag } else { 942 8403 Anurag /* Refetch succeeded, update LIF */ 943 8403 Anurag lif->lif_netmask = 944 8403 Anurag ((struct sockaddr_in *)&lifr.lifr_addr)-> 945 8403 Anurag sin_addr.s_addr; 946 8403 Anurag } 947 3431 carlsonj } 948 3431 carlsonj } 949 3431 carlsonj } 950 3431 carlsonj 951 3431 carlsonj /* 952 3431 carlsonj * plumb_lif(): Adds the LIF to the system. This is used for all 953 3431 carlsonj * DHCPv6-derived interfaces. The returned LIF has a hold 954 9629 james * on it. The caller (configure_v6_leases) deals with the DAD 955 9629 james * wait counters. 956 3431 carlsonj * 957 3431 carlsonj * input: dhcp_lif_t *: the interface to unplumb 958 3431 carlsonj * output: none 959 3431 carlsonj */ 960 3431 carlsonj 961 3431 carlsonj dhcp_lif_t * 962 3431 carlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr) 963 3431 carlsonj { 964 3431 carlsonj dhcp_lif_t *lif; 965 3431 carlsonj char abuf[INET6_ADDRSTRLEN]; 966 3431 carlsonj struct lifreq lifr; 967 3431 carlsonj struct sockaddr_in6 *sin6; 968 3431 carlsonj int error; 969 3431 carlsonj 970 3431 carlsonj (void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)); 971 3431 carlsonj 972 3431 carlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 973 3431 carlsonj if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) { 974 3431 carlsonj dhcpmsg(MSG_ERR, 975 3431 carlsonj "plumb_lif: entry for %s already exists!", abuf); 976 3431 carlsonj return (NULL); 977 3431 carlsonj } 978 3431 carlsonj } 979 3431 carlsonj 980 3431 carlsonj /* First, create a new zero-address logical interface */ 981 3431 carlsonj (void) memset(&lifr, 0, sizeof (lifr)); 982 3431 carlsonj (void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name)); 983 3431 carlsonj if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) { 984 3431 carlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name); 985 3431 carlsonj return (NULL); 986 3431 carlsonj } 987 3431 carlsonj 988 3431 carlsonj /* Next, set the netmask to all ones */ 989 3431 carlsonj sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 990 3431 carlsonj sin6->sin6_family = AF_INET6; 991 3431 carlsonj (void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr)); 992 3431 carlsonj if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 993 3431 carlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s", 994 3431 carlsonj lifr.lifr_name); 995 3431 carlsonj goto failure; 996 3431 carlsonj } 997 3431 carlsonj 998 3431 carlsonj /* Now set the interface address */ 999 3431 carlsonj sin6->sin6_addr = *addr; 1000 3431 carlsonj if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 1001 3431 carlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s", 1002 3431 carlsonj lifr.lifr_name, abuf); 1003 3431 carlsonj goto failure; 1004 3431 carlsonj } 1005 3431 carlsonj 1006 3431 carlsonj /* Mark the interface up */ 1007 3431 carlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 1008 3431 carlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s", 1009 3431 carlsonj lifr.lifr_name); 1010 3431 carlsonj goto failure; 1011 3431 carlsonj } 1012 8485 Peter 1013 8485 Peter /* 1014 8485 Peter * See comment in set_lif_dhcp(). 1015 8485 Peter */ 1016 8485 Peter if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER)) 1017 8485 Peter lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED; 1018 8485 Peter 1019 3431 carlsonj lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING; 1020 3431 carlsonj if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 1021 3431 carlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s", 1022 3431 carlsonj lifr.lifr_name); 1023 3431 carlsonj goto failure; 1024 3431 carlsonj } 1025 3431 carlsonj 1026 3431 carlsonj /* Now we can create the internal LIF structure */ 1027 3431 carlsonj hold_pif(pif); 1028 3431 carlsonj if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL) 1029 3431 carlsonj goto failure; 1030 3431 carlsonj 1031 3431 carlsonj dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf, 1032 3431 carlsonj lif->lif_name); 1033 3431 carlsonj lif->lif_plumbed = B_TRUE; 1034 3431 carlsonj 1035 3431 carlsonj return (lif); 1036 3431 carlsonj 1037 3431 carlsonj failure: 1038 3431 carlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 1039 3431 carlsonj errno != ENXIO) { 1040 3431 carlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s", 1041 3431 carlsonj lifr.lifr_name); 1042 3431 carlsonj } 1043 3431 carlsonj return (NULL); 1044 3431 carlsonj } 1045 3431 carlsonj 1046 3431 carlsonj /* 1047 3431 carlsonj * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used 1048 3431 carlsonj * for all interfaces configured by DHCP (those in leases). 1049 3431 carlsonj * 1050 3431 carlsonj * input: dhcp_lif_t *: the interface to unplumb 1051 3431 carlsonj * output: none 1052 3431 carlsonj */ 1053 3431 carlsonj 1054 3431 carlsonj void 1055 3431 carlsonj unplumb_lif(dhcp_lif_t *lif) 1056 3431 carlsonj { 1057 3431 carlsonj dhcp_lease_t *dlp; 1058 3431 carlsonj 1059 3431 carlsonj if (lif->lif_plumbed) { 1060 3431 carlsonj struct lifreq lifr; 1061 3431 carlsonj 1062 3431 carlsonj (void) memset(&lifr, 0, sizeof (lifr)); 1063 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, 1064 3431 carlsonj sizeof (lifr.lifr_name)); 1065 3431 carlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 1066 3431 carlsonj errno != ENXIO) { 1067 3431 carlsonj dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s", 1068 3431 carlsonj lif->lif_name); 1069 3431 carlsonj } 1070 3431 carlsonj lif->lif_plumbed = B_FALSE; 1071 3431 carlsonj } 1072 5978 meem 1073 3431 carlsonj /* 1074 3431 carlsonj * Special case: if we're "unplumbing" the main LIF for DHCPv4, then 1075 9629 james * just canonize it and remove it from the lease. The DAD wait flags 1076 9629 james * are handled by canonize_lif or by remove_lif. 1077 3431 carlsonj */ 1078 3431 carlsonj if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) { 1079 5381 meem canonize_lif(lif, B_TRUE); 1080 3431 carlsonj cancel_lif_timers(lif); 1081 3431 carlsonj if (lif->lif_declined != NULL) { 1082 3431 carlsonj dlp->dl_smach->dsm_lif_down--; 1083 3431 carlsonj lif->lif_declined = NULL; 1084 3431 carlsonj } 1085 3431 carlsonj dlp->dl_nlifs = 0; 1086 3431 carlsonj dlp->dl_lifs = NULL; 1087 3431 carlsonj lif->lif_lease = NULL; 1088 3431 carlsonj release_lif(lif); 1089 3431 carlsonj } else { 1090 3431 carlsonj remove_lif(lif); 1091 3431 carlsonj } 1092 3431 carlsonj } 1093 3431 carlsonj 1094 3431 carlsonj /* 1095 3431 carlsonj * attach_lif(): create a new logical interface, creating the physical 1096 3431 carlsonj * interface as necessary. 1097 3431 carlsonj * 1098 3431 carlsonj * input: const char *: the logical interface name 1099 3431 carlsonj * boolean_t: B_TRUE for IPv6 1100 3431 carlsonj * int *: set to DHCP_IPC_E_* if creation fails 1101 3431 carlsonj * output: dhcp_lif_t *: pointer to new entry, or NULL on failure 1102 3431 carlsonj */ 1103 3431 carlsonj 1104 3431 carlsonj dhcp_lif_t * 1105 3431 carlsonj attach_lif(const char *lname, boolean_t isv6, int *error) 1106 3431 carlsonj { 1107 3431 carlsonj dhcp_pif_t *pif; 1108 3431 carlsonj char pname[LIFNAMSIZ], *cp; 1109 3431 carlsonj 1110 3431 carlsonj (void) strlcpy(pname, lname, sizeof (pname)); 1111 3431 carlsonj if ((cp = strchr(pname, ':')) != NULL) 1112 3431 carlsonj *cp = '\0'; 1113 3431 carlsonj 1114 3431 carlsonj if ((pif = lookup_pif_by_name(pname, isv6)) != NULL) 1115 3431 carlsonj hold_pif(pif); 1116 3431 carlsonj else if ((pif = insert_pif(pname, isv6, error)) == NULL) 1117 3431 carlsonj return (NULL); 1118 3431 carlsonj 1119 3431 carlsonj if (lookup_lif_by_name(lname, pif) != NULL) { 1120 3431 carlsonj dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!", 1121 3431 carlsonj lname); 1122 3431 carlsonj release_pif(pif); 1123 3431 carlsonj *error = DHCP_IPC_E_INVIF; 1124 3431 carlsonj return (NULL); 1125 3431 carlsonj } 1126 3431 carlsonj 1127 3431 carlsonj /* If LIF creation fails, then insert_lif discards our PIF hold */ 1128 3431 carlsonj return (insert_lif(pif, lname, error)); 1129 3431 carlsonj } 1130 3431 carlsonj 1131 3431 carlsonj /* 1132 3431 carlsonj * set_lif_dhcp(): Set logical interface flags to show that it's managed 1133 3431 carlsonj * by DHCP. 1134 3431 carlsonj * 1135 3431 carlsonj * input: dhcp_lif_t *: the logical interface 1136 3431 carlsonj * output: int: set to DHCP_IPC_E_* if operation fails 1137 3431 carlsonj */ 1138 3431 carlsonj 1139 3431 carlsonj int 1140 10785 Peter set_lif_dhcp(dhcp_lif_t *lif) 1141 3431 carlsonj { 1142 3431 carlsonj int fd; 1143 3431 carlsonj int err; 1144 3431 carlsonj struct lifreq lifr; 1145 8485 Peter dhcp_pif_t *pif = lif->lif_pif; 1146 3431 carlsonj 1147 8485 Peter fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1148 3431 carlsonj 1149 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1150 3431 carlsonj 1151 3431 carlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 1152 3431 carlsonj err = errno; 1153 3431 carlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s", 1154 3431 carlsonj lif->lif_name); 1155 3431 carlsonj return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT); 1156 3431 carlsonj } 1157 3431 carlsonj lif->lif_flags = lifr.lifr_flags; 1158 3431 carlsonj 1159 3431 carlsonj /* 1160 3431 carlsonj * Check for conflicting sources of address control, and other 1161 3431 carlsonj * unacceptable configurations. 1162 3431 carlsonj */ 1163 4823 seb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 1164 4823 seb IFF_VIRTUAL)) { 1165 3431 carlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx", 1166 3431 carlsonj lif->lif_name, lifr.lifr_flags); 1167 3431 carlsonj return (DHCP_IPC_E_INVIF); 1168 0 stevel } 1169 0 stevel 1170 0 stevel /* 1171 10785 Peter * If IFF_DHCPRUNNING is already set on the interface and we're not 1172 10785 Peter * adopting it, the agent probably crashed and burned. Note it, but 1173 10785 Peter * don't let it stop the proceedings (we're pretty sure we're not 1174 10785 Peter * already running, since we were able to bind to our IPC port). 1175 0 stevel */ 1176 3431 carlsonj if (lifr.lifr_flags & IFF_DHCPRUNNING) { 1177 10785 Peter dhcpmsg(MSG_VERBOSE, "set_lif_dhcp: IFF_DHCPRUNNING already set" 1178 10785 Peter " on %s", lif->lif_name); 1179 3431 carlsonj } else { 1180 8485 Peter /* 1181 8485 Peter * If the lif is on an interface under IPMP, IFF_NOFAILOVER 1182 8485 Peter * must be set or the kernel will prevent us from setting 1183 8485 Peter * IFF_DHCPRUNNING (since the subsequent IFF_UP would lead to 1184 8485 Peter * migration). We set IFF_DEPRECATED too since the kernel 1185 8485 Peter * will set it automatically when setting IFF_NOFAILOVER, 1186 8485 Peter * causing our lif_flags value to grow stale. 1187 8485 Peter */ 1188 8485 Peter if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER)) 1189 8485 Peter lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED; 1190 8485 Peter 1191 3431 carlsonj lifr.lifr_flags |= IFF_DHCPRUNNING; 1192 3431 carlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 1193 3431 carlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s", 1194 3431 carlsonj lif->lif_name); 1195 3431 carlsonj return (DHCP_IPC_E_INT); 1196 3431 carlsonj } 1197 3431 carlsonj lif->lif_flags = lifr.lifr_flags; 1198 3431 carlsonj } 1199 3431 carlsonj return (DHCP_IPC_SUCCESS); 1200 3431 carlsonj } 1201 0 stevel 1202 3431 carlsonj /* 1203 3431 carlsonj * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer 1204 3431 carlsonj * managed by DHCP. 1205 3431 carlsonj * 1206 3431 carlsonj * input: dhcp_lif_t *: the logical interface 1207 3431 carlsonj * output: none 1208 3431 carlsonj */ 1209 0 stevel 1210 3431 carlsonj static void 1211 3431 carlsonj clear_lif_dhcp(dhcp_lif_t *lif) 1212 3431 carlsonj { 1213 3431 carlsonj int fd; 1214 3431 carlsonj struct lifreq lifr; 1215 0 stevel 1216 3431 carlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1217 0 stevel 1218 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1219 0 stevel 1220 3431 carlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 1221 3431 carlsonj return; 1222 0 stevel 1223 3431 carlsonj if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) 1224 3431 carlsonj return; 1225 0 stevel 1226 3431 carlsonj lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING; 1227 3431 carlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 1228 3431 carlsonj } 1229 0 stevel 1230 3431 carlsonj /* 1231 3431 carlsonj * set_lif_deprecated(): Set the "deprecated" flag to tell users that this 1232 3431 carlsonj * address will be going away. As the interface is 1233 3431 carlsonj * going away, we don't care if there are errors. 1234 3431 carlsonj * 1235 3431 carlsonj * input: dhcp_lif_t *: the logical interface 1236 3431 carlsonj * output: none 1237 3431 carlsonj */ 1238 0 stevel 1239 3431 carlsonj void 1240 3431 carlsonj set_lif_deprecated(dhcp_lif_t *lif) 1241 3431 carlsonj { 1242 3431 carlsonj int fd; 1243 3431 carlsonj struct lifreq lifr; 1244 0 stevel 1245 3431 carlsonj if (lif->lif_flags & IFF_DEPRECATED) 1246 3431 carlsonj return; 1247 0 stevel 1248 3431 carlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1249 0 stevel 1250 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1251 0 stevel 1252 3431 carlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 1253 3431 carlsonj return; 1254 0 stevel 1255 3431 carlsonj if (lifr.lifr_flags & IFF_DEPRECATED) 1256 3431 carlsonj return; 1257 0 stevel 1258 3431 carlsonj lifr.lifr_flags |= IFF_DEPRECATED; 1259 3431 carlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 1260 3431 carlsonj lif->lif_flags = lifr.lifr_flags; 1261 3431 carlsonj } 1262 0 stevel 1263 3431 carlsonj /* 1264 3431 carlsonj * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this 1265 3431 carlsonj * address will not be going away. This happens if we 1266 3431 carlsonj * get a renewal after preferred lifetime but before 1267 3431 carlsonj * the valid lifetime. 1268 3431 carlsonj * 1269 3431 carlsonj * input: dhcp_lif_t *: the logical interface 1270 3431 carlsonj * output: boolean_t: B_TRUE on success. 1271 3431 carlsonj */ 1272 0 stevel 1273 3431 carlsonj boolean_t 1274 3431 carlsonj clear_lif_deprecated(dhcp_lif_t *lif) 1275 3431 carlsonj { 1276 3431 carlsonj int fd; 1277 3431 carlsonj struct lifreq lifr; 1278 3431 carlsonj 1279 3431 carlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1280 3431 carlsonj 1281 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1282 3431 carlsonj 1283 3431 carlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 1284 3431 carlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s", 1285 3431 carlsonj lif->lif_name); 1286 3431 carlsonj return (B_FALSE); 1287 0 stevel } 1288 0 stevel 1289 0 stevel /* 1290 3431 carlsonj * Check for conflicting sources of address control, and other 1291 3431 carlsonj * unacceptable configurations. 1292 0 stevel */ 1293 4823 seb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 1294 4823 seb IFF_VIRTUAL)) { 1295 3431 carlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags " 1296 3431 carlsonj "are %llx", lif->lif_name, lifr.lifr_flags); 1297 3431 carlsonj return (B_FALSE); 1298 0 stevel } 1299 0 stevel 1300 8485 Peter /* 1301 8485 Peter * Don't try to clear IFF_DEPRECATED if this is a test address, 1302 8485 Peter * since IPMP's use of IFF_DEPRECATED is not compatible with ours. 1303 8485 Peter */ 1304 8485 Peter if (lifr.lifr_flags & IFF_NOFAILOVER) 1305 8485 Peter return (B_TRUE); 1306 8485 Peter 1307 3431 carlsonj if (!(lifr.lifr_flags & IFF_DEPRECATED)) 1308 3431 carlsonj return (B_TRUE); 1309 0 stevel 1310 3431 carlsonj lifr.lifr_flags &= ~IFF_DEPRECATED; 1311 3431 carlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 1312 3431 carlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s", 1313 3431 carlsonj lif->lif_name); 1314 3431 carlsonj return (B_FALSE); 1315 3431 carlsonj } else { 1316 3431 carlsonj lif->lif_flags = lifr.lifr_flags; 1317 3431 carlsonj return (B_TRUE); 1318 0 stevel } 1319 0 stevel } 1320 0 stevel 1321 0 stevel /* 1322 3431 carlsonj * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only). 1323 0 stevel * 1324 3431 carlsonj * input: dhcp_lif_t *: the logical interface to operate on 1325 5381 meem * in_addr_t: the address the socket will be bound to (in hbo) 1326 8485 Peter * boolean_t: B_TRUE if the address should be brought up (if needed) 1327 3431 carlsonj * output: boolean_t: B_TRUE if the socket was opened successfully. 1328 0 stevel */ 1329 0 stevel 1330 3431 carlsonj boolean_t 1331 8485 Peter open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo, boolean_t bringup) 1332 0 stevel { 1333 5381 meem const char *errmsg; 1334 5381 meem struct lifreq lifr; 1335 5381 meem int on = 1; 1336 5455 meem uchar_t ttl = 255; 1337 8485 Peter uint32_t ifindex; 1338 8485 Peter dhcp_pif_t *pif = lif->lif_pif; 1339 5381 meem 1340 3431 carlsonj if (lif->lif_sock_ip_fd != -1) { 1341 3431 carlsonj dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s", 1342 3431 carlsonj lif->lif_name); 1343 3431 carlsonj return (B_FALSE); 1344 0 stevel } 1345 0 stevel 1346 3431 carlsonj lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0); 1347 3431 carlsonj if (lif->lif_sock_ip_fd == -1) { 1348 5381 meem errmsg = "cannot create v4 socket"; 1349 5381 meem goto failure; 1350 2157 dh155122 } 1351 2157 dh155122 1352 5381 meem if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) { 1353 5381 meem errmsg = "cannot bind v4 socket"; 1354 5381 meem goto failure; 1355 0 stevel } 1356 0 stevel 1357 5381 meem /* 1358 5381 meem * If we bound to INADDR_ANY, we have no IFF_UP source address to use. 1359 5381 meem * Thus, enable IP_UNSPEC_SRC so that we can send packets with an 1360 5381 meem * unspecified (0.0.0.0) address. Also, enable IP_DHCPINIT_IF so that 1361 5381 meem * the IP module will accept unicast DHCP traffic regardless of the IP 1362 5381 meem * address it's sent to. (We'll then figure out which packets are 1363 5381 meem * ours based on the xid.) 1364 5381 meem */ 1365 5381 meem if (addr_hbo == INADDR_ANY) { 1366 5381 meem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC, 1367 5381 meem &on, sizeof (int)) == -1) { 1368 5381 meem errmsg = "cannot set IP_UNSPEC_SRC"; 1369 5381 meem goto failure; 1370 5381 meem } 1371 5381 meem 1372 5381 meem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF, 1373 8485 Peter &pif->pif_index, sizeof (int)) == -1) { 1374 5381 meem errmsg = "cannot set IP_DHCPINIT_IF"; 1375 5381 meem goto failure; 1376 5381 meem } 1377 5455 meem } 1378 5455 meem 1379 5455 meem /* 1380 5455 meem * Unfortunately, some hardware (such as the Linksys WRT54GC) 1381 5455 meem * decrements the TTL *prior* to accepting DHCP traffic destined 1382 5455 meem * for it. To workaround this, tell IP to use a TTL of 255 for 1383 5455 meem * broadcast packets sent from this socket. 1384 5455 meem */ 1385 5455 meem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BROADCAST_TTL, &ttl, 1386 5455 meem sizeof (uchar_t)) == -1) { 1387 5455 meem errmsg = "cannot set IP_BROADCAST_TTL"; 1388 5455 meem goto failure; 1389 0 stevel } 1390 5381 meem 1391 8485 Peter ifindex = pif->pif_under_ipmp ? pif->pif_grindex : pif->pif_index; 1392 8485 Peter if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, &ifindex, 1393 8485 Peter sizeof (int)) == -1) { 1394 5381 meem errmsg = "cannot set IP_BOUND_IF"; 1395 5381 meem goto failure; 1396 5381 meem } 1397 5381 meem 1398 5381 meem (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1399 5381 meem if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 1400 5381 meem errmsg = "cannot get interface flags"; 1401 5381 meem goto failure; 1402 5381 meem } 1403 5381 meem 1404 8485 Peter /* 1405 8485 Peter * If the lif is part of an interface under IPMP, IFF_NOFAILOVER must 1406 8485 Peter * be set or the kernel will prevent us from setting IFF_DHCPRUNNING 1407 8485 Peter * (since the subsequent IFF_UP would lead to migration). We set 1408 8485 Peter * IFF_DEPRECATED too since the kernel will set it automatically when 1409 8485 Peter * setting IFF_NOFAILOVER, causing our lif_flags value to grow stale. 1410 8485 Peter */ 1411 8485 Peter if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER)) { 1412 8485 Peter lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED; 1413 8485 Peter if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 1414 8485 Peter errmsg = "cannot set IFF_NOFAILOVER"; 1415 8485 Peter goto failure; 1416 8485 Peter } 1417 8485 Peter } 1418 8485 Peter lif->lif_flags = lifr.lifr_flags; 1419 8485 Peter 1420 8485 Peter /* 1421 8485 Peter * If this is initial bringup, make sure the address we're acquiring a 1422 8485 Peter * lease on is IFF_UP. 1423 8485 Peter */ 1424 8485 Peter if (bringup && !(lifr.lifr_flags & IFF_UP)) { 1425 5381 meem /* 1426 5381 meem * Start from a clean slate. 1427 5381 meem */ 1428 5381 meem canonize_lif(lif, B_FALSE); 1429 5381 meem 1430 5381 meem lifr.lifr_flags |= IFF_UP; 1431 5381 meem if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 1432 5381 meem errmsg = "cannot bring up"; 1433 5381 meem goto failure; 1434 5381 meem } 1435 5439 meem lif->lif_flags = lifr.lifr_flags; 1436 5381 meem 1437 5381 meem /* 1438 5381 meem * When bringing 0.0.0.0 IFF_UP, the kernel changes the 1439 5381 meem * netmask to 255.0.0.0, so re-fetch our expected netmask. 1440 5381 meem */ 1441 5381 meem if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) { 1442 5381 meem errmsg = "cannot get netmask"; 1443 5381 meem goto failure; 1444 5381 meem } 1445 5381 meem 1446 5381 meem lif->lif_netmask = 1447 5381 meem ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr; 1448 8485 Peter } 1449 8485 Peter 1450 8485 Peter /* 1451 8485 Peter * Usually, bringing up the address we're acquiring a lease on is 1452 8485 Peter * sufficient to allow packets to be sent and received via the 1453 8485 Peter * IP_BOUND_IF we did earlier. However, if we're acquiring a lease on 1454 8485 Peter * an underlying IPMP interface, the group interface will be used for 1455 8485 Peter * sending and receiving IP packets via IP_BOUND_IF. Thus, ensure at 1456 8485 Peter * least one address on the group interface is IFF_UP. 1457 8485 Peter */ 1458 8485 Peter if (bringup && pif->pif_under_ipmp) { 1459 8485 Peter (void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ); 1460 8485 Peter if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 1461 8485 Peter errmsg = "cannot get IPMP group interface flags"; 1462 8485 Peter goto failure; 1463 8485 Peter } 1464 8485 Peter 1465 8485 Peter if (!(lifr.lifr_flags & IFF_UP)) { 1466 8485 Peter lifr.lifr_flags |= IFF_UP; 1467 8485 Peter if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 1468 8485 Peter errmsg = "cannot bring up IPMP group interface"; 1469 8485 Peter goto failure; 1470 8485 Peter } 1471 8485 Peter } 1472 5381 meem } 1473 5381 meem 1474 5381 meem lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN, 1475 5381 meem dhcp_packet_lif, lif); 1476 5381 meem if (lif->lif_packet_id == -1) { 1477 5381 meem errmsg = "cannot register to receive DHCP packets"; 1478 5381 meem goto failure; 1479 5381 meem } 1480 5381 meem 1481 0 stevel return (B_TRUE); 1482 5381 meem failure: 1483 5381 meem dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg); 1484 5381 meem close_ip_lif(lif); 1485 5381 meem return (B_FALSE); 1486 0 stevel } 1487 0 stevel 1488 0 stevel /* 1489 3431 carlsonj * close_ip_lif(): close an IP socket for I/O on a given LIF. 1490 0 stevel * 1491 3431 carlsonj * input: dhcp_lif_t *: the logical interface to operate on 1492 3431 carlsonj * output: none 1493 0 stevel */ 1494 0 stevel 1495 0 stevel void 1496 3431 carlsonj close_ip_lif(dhcp_lif_t *lif) 1497 0 stevel { 1498 5381 meem if (lif->lif_packet_id != -1) { 1499 5381 meem (void) iu_unregister_event(eh, lif->lif_packet_id, NULL); 1500 5381 meem lif->lif_packet_id = -1; 1501 3431 carlsonj } 1502 3431 carlsonj if (lif->lif_sock_ip_fd != -1) { 1503 3431 carlsonj (void) close(lif->lif_sock_ip_fd); 1504 3431 carlsonj lif->lif_sock_ip_fd = -1; 1505 0 stevel } 1506 0 stevel } 1507 0 stevel 1508 0 stevel /* 1509 3431 carlsonj * lif_mark_decline(): mark a LIF as having been declined due to a duplicate 1510 3431 carlsonj * address or some other conflict. This is used in 1511 3431 carlsonj * send_declines() to report failure back to the server. 1512 0 stevel * 1513 3431 carlsonj * input: dhcp_lif_t *: the logical interface to operate on 1514 3431 carlsonj * const char *: text string explaining why the address is declined 1515 3431 carlsonj * output: none 1516 0 stevel */ 1517 0 stevel 1518 0 stevel void 1519 3431 carlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason) 1520 0 stevel { 1521 3431 carlsonj if (lif->lif_declined == NULL) { 1522 3431 carlsonj dhcp_lease_t *dlp; 1523 0 stevel 1524 3431 carlsonj lif->lif_declined = reason; 1525 3431 carlsonj if ((dlp = lif->lif_lease) != NULL) 1526 3431 carlsonj dlp->dl_smach->dsm_lif_down++; 1527 0 stevel } 1528 0 stevel } 1529 0 stevel 1530 0 stevel /* 1531 3431 carlsonj * schedule_lif_timer(): schedules the LIF-related timer 1532 0 stevel * 1533 3431 carlsonj * input: dhcp_lif_t *: the logical interface to operate on 1534 3431 carlsonj * dhcp_timer_t *: the timer to schedule 1535 3431 carlsonj * iu_tq_callback_t *: the callback to call upon firing 1536 3431 carlsonj * output: boolean_t: B_TRUE if the timer was scheduled successfully 1537 0 stevel */ 1538 0 stevel 1539 3431 carlsonj boolean_t 1540 3431 carlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire) 1541 0 stevel { 1542 3431 carlsonj /* 1543 3431 carlsonj * If there's a timer running, cancel it and release its lease 1544 3431 carlsonj * reference. 1545 3431 carlsonj */ 1546 3431 carlsonj if (dt->dt_id != -1) { 1547 3431 carlsonj if (!cancel_timer(dt)) 1548 3431 carlsonj return (B_FALSE); 1549 3431 carlsonj release_lif(lif); 1550 3431 carlsonj } 1551 0 stevel 1552 3431 carlsonj if (schedule_timer(dt, expire, lif)) { 1553 3431 carlsonj hold_lif(lif); 1554 3431 carlsonj return (B_TRUE); 1555 3431 carlsonj } else { 1556 3431 carlsonj dhcpmsg(MSG_WARNING, 1557 3431 carlsonj "schedule_lif_timer: cannot schedule timer"); 1558 3431 carlsonj return (B_FALSE); 1559 0 stevel } 1560 0 stevel } 1561 0 stevel 1562 0 stevel /* 1563 3431 carlsonj * cancel_lif_timer(): cancels a LIF-related timer 1564 0 stevel * 1565 3431 carlsonj * input: dhcp_lif_t *: the logical interface to operate on 1566 3431 carlsonj * dhcp_timer_t *: the timer to cancel 1567 3431 carlsonj * output: none 1568 3431 carlsonj */ 1569 3431 carlsonj 1570 3431 carlsonj static void 1571 3431 carlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt) 1572 3431 carlsonj { 1573 3431 carlsonj if (dt->dt_id == -1) 1574 3431 carlsonj return; 1575 3431 carlsonj if (cancel_timer(dt)) { 1576 3431 carlsonj dhcpmsg(MSG_DEBUG2, 1577 3431 carlsonj "cancel_lif_timer: canceled expiry timer on %s", 1578 3431 carlsonj lif->lif_name); 1579 3431 carlsonj release_lif(lif); 1580 3431 carlsonj } else { 1581 3431 carlsonj dhcpmsg(MSG_WARNING, 1582 3431 carlsonj "cancel_lif_timer: cannot cancel timer on %s", 1583 3431 carlsonj lif->lif_name); 1584 3431 carlsonj } 1585 3431 carlsonj } 1586 3431 carlsonj 1587 3431 carlsonj /* 1588 3431 carlsonj * cancel_lif_timers(): cancels the LIF-related timers 1589 3431 carlsonj * 1590 3431 carlsonj * input: dhcp_lif_t *: the logical interface to operate on 1591 3431 carlsonj * output: none 1592 0 stevel */ 1593 0 stevel 1594 0 stevel void 1595 3431 carlsonj cancel_lif_timers(dhcp_lif_t *lif) 1596 0 stevel { 1597 3431 carlsonj cancel_lif_timer(lif, &lif->lif_preferred); 1598 3431 carlsonj cancel_lif_timer(lif, &lif->lif_expire); 1599 0 stevel } 1600 0 stevel 1601 0 stevel /* 1602 3431 carlsonj * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common 1603 3431 carlsonj * file descriptors (v4_sock_fd and v6_sock_fd). 1604 0 stevel * 1605 3431 carlsonj * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4 1606 3431 carlsonj * output: none 1607 0 stevel */ 1608 0 stevel 1609 3431 carlsonj uint_t 1610 3431 carlsonj get_max_mtu(boolean_t isv6) 1611 0 stevel { 1612 3431 carlsonj uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu; 1613 0 stevel 1614 3431 carlsonj if (*mtup == 0) { 1615 3431 carlsonj dhcp_pif_t *pif; 1616 3431 carlsonj dhcp_lif_t *lif; 1617 3431 carlsonj struct lifreq lifr; 1618 3431 carlsonj 1619 3431 carlsonj /* Set an arbitrary lower bound */ 1620 3431 carlsonj *mtup = 1024; 1621 3431 carlsonj pif = isv6 ? v6root : v4root; 1622 3431 carlsonj for (; pif != NULL; pif = pif->pif_next) { 1623 3431 carlsonj for (lif = pif->pif_lifs; lif != NULL; 1624 3431 carlsonj lif = lif->lif_next) { 1625 3431 carlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, 1626 3431 carlsonj LIFNAMSIZ); 1627 3431 carlsonj if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) != 1628 3431 carlsonj -1 && lifr.lifr_mtu > *mtup) { 1629 3431 carlsonj *mtup = lifr.lifr_mtu; 1630 3431 carlsonj } 1631 3431 carlsonj } 1632 3431 carlsonj } 1633 0 stevel } 1634 3431 carlsonj return (*mtup); 1635 0 stevel } 1636 0 stevel 1637 0 stevel /* 1638 3431 carlsonj * expired_lif_state(): summarize the state of expired LIFs on a given state 1639 3431 carlsonj * machine. 1640 0 stevel * 1641 3431 carlsonj * input: dhcp_smach_t *: the state machine to scan 1642 3431 carlsonj * output: dhcp_expire_t: overall state 1643 0 stevel */ 1644 0 stevel 1645 3431 carlsonj dhcp_expire_t 1646 3431 carlsonj expired_lif_state(dhcp_smach_t *dsmp) 1647 0 stevel { 1648 3431 carlsonj dhcp_lease_t *dlp; 1649 3431 carlsonj dhcp_lif_t *lif; 1650 3431 carlsonj uint_t nlifs; 1651 3431 carlsonj uint_t numlifs; 1652 3431 carlsonj uint_t numexp; 1653 3431 carlsonj 1654 3431 carlsonj numlifs = numexp = 0; 1655 3431 carlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1656 3431 carlsonj lif = dlp->dl_lifs; 1657 3431 carlsonj nlifs = dlp->dl_nlifs; 1658 3431 carlsonj numlifs += nlifs; 1659 3431 carlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 1660 3431 carlsonj if (lif->lif_expired) 1661 3431 carlsonj numexp++; 1662 3431 carlsonj } 1663 3431 carlsonj } 1664 3431 carlsonj if (numlifs == 0) 1665 3431 carlsonj return (DHCP_EXP_NOLIFS); 1666 3431 carlsonj else if (numexp == 0) 1667 3431 carlsonj return (DHCP_EXP_NOEXP); 1668 3431 carlsonj else if (numlifs == numexp) 1669 3431 carlsonj return (DHCP_EXP_ALLEXP); 1670 3431 carlsonj else 1671 3431 carlsonj return (DHCP_EXP_SOMEEXP); 1672 3431 carlsonj } 1673 3431 carlsonj 1674 3431 carlsonj /* 1675 3431 carlsonj * find_expired_lif(): find the first expired LIF on a given state machine 1676 3431 carlsonj * 1677 3431 carlsonj * input: dhcp_smach_t *: the state machine to scan 1678 3431 carlsonj * output: dhcp_lif_t *: the first expired LIF, or NULL if none. 1679 3431 carlsonj */ 1680 3431 carlsonj 1681 3431 carlsonj dhcp_lif_t * 1682 3431 carlsonj find_expired_lif(dhcp_smach_t *dsmp) 1683 3431 carlsonj { 1684 3431 carlsonj dhcp_lease_t *dlp; 1685 3431 carlsonj dhcp_lif_t *lif; 1686 3431 carlsonj uint_t nlifs; 1687 3431 carlsonj 1688 3431 carlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1689 3431 carlsonj lif = dlp->dl_lifs; 1690 3431 carlsonj nlifs = dlp->dl_nlifs; 1691 3431 carlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 1692 3431 carlsonj if (lif->lif_expired) 1693 3431 carlsonj return (lif); 1694 3431 carlsonj } 1695 3431 carlsonj } 1696 3431 carlsonj return (NULL); 1697 3431 carlsonj } 1698 3431 carlsonj 1699 3431 carlsonj /* 1700 3431 carlsonj * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used 1701 3431 carlsonj * only for DHCPv6. 1702 3431 carlsonj * 1703 3431 carlsonj * input: none 1704 3431 carlsonj * output: none 1705 3431 carlsonj */ 1706 3431 carlsonj 1707 3431 carlsonj void 1708 3431 carlsonj remove_v6_strays(void) 1709 3431 carlsonj { 1710 3431 carlsonj struct lifnum lifn; 1711 3431 carlsonj struct lifconf lifc; 1712 3431 carlsonj struct lifreq *lifrp, *lifrmax; 1713 3431 carlsonj uint_t numifs; 1714 3431 carlsonj uint64_t flags; 1715 0 stevel 1716 0 stevel /* 1717 3431 carlsonj * Get the approximate number of interfaces in the system. It's only 1718 3431 carlsonj * approximate because the system is dynamic -- interfaces may be 1719 3431 carlsonj * plumbed or unplumbed at any time. This is also the reason for the 1720 3431 carlsonj * "+ 10" fudge factor: we're trying to avoid unnecessary looping. 1721 0 stevel */ 1722 3431 carlsonj (void) memset(&lifn, 0, sizeof (lifn)); 1723 3431 carlsonj lifn.lifn_family = AF_INET6; 1724 3431 carlsonj lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 1725 3431 carlsonj if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) { 1726 3431 carlsonj dhcpmsg(MSG_ERR, 1727 3431 carlsonj "remove_v6_strays: cannot read number of interfaces"); 1728 3431 carlsonj numifs = 10; 1729 3431 carlsonj } else { 1730 3431 carlsonj numifs = lifn.lifn_count + 10; 1731 0 stevel } 1732 0 stevel 1733 0 stevel /* 1734 3431 carlsonj * Get the interface information. We do this in a loop so that we can 1735 3431 carlsonj * recover from EINVAL from the kernel -- delivered when the buffer is 1736 3431 carlsonj * too small. 1737 0 stevel */ 1738 3431 carlsonj (void) memset(&lifc, 0, sizeof (lifc)); 1739 3431 carlsonj lifc.lifc_family = AF_INET6; 1740 3431 carlsonj lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 1741 3431 carlsonj for (;;) { 1742 3431 carlsonj lifc.lifc_len = numifs * sizeof (*lifrp); 1743 3431 carlsonj lifrp = realloc(lifc.lifc_buf, lifc.lifc_len); 1744 3431 carlsonj if (lifrp == NULL) { 1745 3431 carlsonj dhcpmsg(MSG_ERR, 1746 3431 carlsonj "remove_v6_strays: cannot allocate memory"); 1747 3431 carlsonj free(lifc.lifc_buf); 1748 3431 carlsonj return; 1749 3431 carlsonj } 1750 3431 carlsonj lifc.lifc_buf = (caddr_t)lifrp; 1751 3431 carlsonj errno = 0; 1752 3431 carlsonj if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 && 1753 3431 carlsonj lifc.lifc_len < numifs * sizeof (*lifrp)) 1754 0 stevel break; 1755 3431 carlsonj if (errno == 0 || errno == EINVAL) { 1756 3431 carlsonj numifs <<= 1; 1757 3431 carlsonj } else { 1758 3431 carlsonj dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF"); 1759 3431 carlsonj free(lifc.lifc_buf); 1760 3431 carlsonj return; 1761 0 stevel } 1762 0 stevel } 1763 0 stevel 1764 3431 carlsonj lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp); 1765 3431 carlsonj for (; lifrp < lifrmax; lifrp++) { 1766 3431 carlsonj /* 1767 3431 carlsonj * Get the interface flags; we're interested in the DHCP ones. 1768 3431 carlsonj */ 1769 3431 carlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1) 1770 3431 carlsonj continue; 1771 3431 carlsonj flags = lifrp->lifr_flags; 1772 3431 carlsonj if (!(flags & IFF_DHCPRUNNING)) 1773 3431 carlsonj continue; 1774 3431 carlsonj /* 1775 3431 carlsonj * If the interface has a link-local address, then we don't 1776 3431 carlsonj * control it. Just remove the flag. 1777 3431 carlsonj */ 1778 3431 carlsonj if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1) 1779 3431 carlsonj continue; 1780 3431 carlsonj if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp-> 1781 3431 carlsonj lifr_addr)->sin6_addr)) { 1782 3431 carlsonj lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING; 1783 3431 carlsonj (void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp); 1784 3431 carlsonj continue; 1785 3431 carlsonj } 1786 3431 carlsonj /* 1787 3431 carlsonj * All others are (or were) under our control. Clean up by 1788 3431 carlsonj * removing them. 1789 3431 carlsonj */ 1790 3431 carlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) { 1791 3431 carlsonj dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s", 1792 3431 carlsonj lifrp->lifr_name); 1793 3431 carlsonj } else if (errno != ENXIO) { 1794 3431 carlsonj dhcpmsg(MSG_ERR, 1795 3431 carlsonj "remove_v6_strays: SIOCLIFREMOVEIF %s", 1796 3431 carlsonj lifrp->lifr_name); 1797 0 stevel } 1798 0 stevel } 1799 3431 carlsonj free(lifc.lifc_buf); 1800 0 stevel } 1801