10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52157Sdh155122 * Common Development and Distribution License (the "License"). 62157Sdh155122 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*3431Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <sys/socket.h> 300Sstevel@tonic-gate #include <net/if.h> 310Sstevel@tonic-gate #include <sys/dlpi.h> 320Sstevel@tonic-gate #include <stdlib.h> 330Sstevel@tonic-gate #include <sys/sockio.h> 340Sstevel@tonic-gate #include <netinet/in.h> 350Sstevel@tonic-gate #include <netinet/dhcp.h> 360Sstevel@tonic-gate #include <string.h> 370Sstevel@tonic-gate #include <unistd.h> 38*3431Scarlsonj #include <search.h> 39*3431Scarlsonj #include <libdevinfo.h> 400Sstevel@tonic-gate #include <netinet/if_ether.h> 41*3431Scarlsonj #include <arpa/inet.h> 420Sstevel@tonic-gate #include <dhcpmsg.h> 43*3431Scarlsonj #include <dhcp_inittab.h> 440Sstevel@tonic-gate 45*3431Scarlsonj #include "agent.h" 460Sstevel@tonic-gate #include "interface.h" 470Sstevel@tonic-gate #include "util.h" 480Sstevel@tonic-gate #include "dlpi_io.h" 490Sstevel@tonic-gate #include "packet.h" 500Sstevel@tonic-gate #include "states.h" 51*3431Scarlsonj 52*3431Scarlsonj dhcp_pif_t *v4root; 53*3431Scarlsonj dhcp_pif_t *v6root; 54*3431Scarlsonj 55*3431Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu; 560Sstevel@tonic-gate 570Sstevel@tonic-gate /* 58*3431Scarlsonj * Interface flags to watch: things that should be under our direct control. 590Sstevel@tonic-gate */ 60*3431Scarlsonj #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | \ 61*3431Scarlsonj IFF_ADDRCONF | IFF_MIPRUNNING | IFF_TEMPORARY) 620Sstevel@tonic-gate 63*3431Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *); 640Sstevel@tonic-gate 650Sstevel@tonic-gate /* 66*3431Scarlsonj * insert_pif(): creates a new physical interface structure and chains it on 67*3431Scarlsonj * the list. Initializes state that remains consistent across 68*3431Scarlsonj * all use of the physical interface entry. 690Sstevel@tonic-gate * 70*3431Scarlsonj * input: const char *: the name of the physical interface 71*3431Scarlsonj * boolean_t: if B_TRUE, this is DHCPv6 72*3431Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 730Sstevel@tonic-gate * error code with the reason why 74*3431Scarlsonj * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure 750Sstevel@tonic-gate */ 760Sstevel@tonic-gate 77*3431Scarlsonj dhcp_pif_t * 78*3431Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error) 790Sstevel@tonic-gate { 80*3431Scarlsonj dhcp_pif_t *pif; 81*3431Scarlsonj struct lifreq lifr; 820Sstevel@tonic-gate 83*3431Scarlsonj if ((pif = calloc(1, sizeof (*pif))) == NULL) { 84*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for " 85*3431Scarlsonj "%s", pname); 86*3431Scarlsonj *error = DHCP_IPC_E_MEMORY; 870Sstevel@tonic-gate return (NULL); 880Sstevel@tonic-gate } 890Sstevel@tonic-gate 90*3431Scarlsonj pif->pif_isv6 = isv6; 91*3431Scarlsonj pif->pif_dlpi_fd = -1; 92*3431Scarlsonj pif->pif_dlpi_id = -1; 93*3431Scarlsonj pif->pif_hold_count = 1; 94*3431Scarlsonj pif->pif_running = B_TRUE; 95*3431Scarlsonj 96*3431Scarlsonj if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) { 97*3431Scarlsonj dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long", 98*3431Scarlsonj pname); 99*3431Scarlsonj *error = DHCP_IPC_E_INVIF; 100*3431Scarlsonj goto failure; 101*3431Scarlsonj } 102*3431Scarlsonj 103*3431Scarlsonj /* We do not use DLPI with DHCPv6 */ 104*3431Scarlsonj if (!isv6) { 105*3431Scarlsonj uint32_t buf[DLPI_BUF_MAX / sizeof (uint32_t)]; 106*3431Scarlsonj dl_info_ack_t *dlia = (dl_info_ack_t *)buf; 107*3431Scarlsonj caddr_t dl_addr; 108*3431Scarlsonj 109*3431Scarlsonj /* 110*3431Scarlsonj * Do the allocations necessary for IPv4 DHCP. 111*3431Scarlsonj * 112*3431Scarlsonj * 1. open the interface using DLPI 113*3431Scarlsonj * 2. get the interface max SDU 114*3431Scarlsonj * 3. get the interface hardware type and hardware length 115*3431Scarlsonj * 4. get the interface hardware address 116*3431Scarlsonj * 5. get the interface hardware broadcast address 117*3431Scarlsonj */ 118*3431Scarlsonj 119*3431Scarlsonj /* step 1 */ 120*3431Scarlsonj pif->pif_dlpi_fd = dlpi_open(pname, dlia, sizeof (buf), 121*3431Scarlsonj ETHERTYPE_IP); 122*3431Scarlsonj if (pif->pif_dlpi_fd == -1) { 123*3431Scarlsonj *error = DHCP_IPC_E_INVIF; 124*3431Scarlsonj goto failure; 125*3431Scarlsonj } 126*3431Scarlsonj 127*3431Scarlsonj /* step 2 */ 128*3431Scarlsonj pif->pif_max = dlia->dl_max_sdu; 129*3431Scarlsonj if (pif->pif_max < DHCP_DEF_MAX_SIZE) { 130*3431Scarlsonj dhcpmsg(MSG_ERROR, "insert_pif: %s does not have a " 131*3431Scarlsonj "large enough maximum SDU to support DHCP " 132*3431Scarlsonj "(%u < %u)", pname, pif->pif_max, 133*3431Scarlsonj DHCP_DEF_MAX_SIZE); 134*3431Scarlsonj *error = DHCP_IPC_E_INVIF; 135*3431Scarlsonj goto failure; 136*3431Scarlsonj } 137*3431Scarlsonj 138*3431Scarlsonj /* step 3 */ 139*3431Scarlsonj pif->pif_hwtype = dlpi_to_arp(dlia->dl_mac_type); 140*3431Scarlsonj pif->pif_hwlen = dlia->dl_addr_length - 141*3431Scarlsonj abs(dlia->dl_sap_length); 142*3431Scarlsonj 143*3431Scarlsonj dhcpmsg(MSG_DEBUG, "insert_pif: %s: sdumax %u, hwtype %d, " 144*3431Scarlsonj "hwlen %d", pname, pif->pif_max, pif->pif_hwtype, 145*3431Scarlsonj pif->pif_hwlen); 146*3431Scarlsonj 147*3431Scarlsonj /* step 4 */ 148*3431Scarlsonj if (pif->pif_hwlen > 0) { 149*3431Scarlsonj pif->pif_hwaddr = malloc(pif->pif_hwlen); 150*3431Scarlsonj if (pif->pif_hwaddr == NULL) { 151*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate " 152*3431Scarlsonj "pif_hwaddr for %s", pname); 153*3431Scarlsonj *error = DHCP_IPC_E_MEMORY; 154*3431Scarlsonj goto failure; 155*3431Scarlsonj } 156*3431Scarlsonj } 157*3431Scarlsonj 158*3431Scarlsonj /* 159*3431Scarlsonj * depending on the DLPI device, the sap and hardware addresses 160*3431Scarlsonj * can be in either order within the dlsap address; find the 161*3431Scarlsonj * location of the hardware address using dl_sap_length. see 162*3431Scarlsonj * the DLPI specification for more on this braindamage. 163*3431Scarlsonj */ 164*3431Scarlsonj 165*3431Scarlsonj dl_addr = (caddr_t)dlia + dlia->dl_addr_offset; 166*3431Scarlsonj if (dlia->dl_sap_length > 0) { 167*3431Scarlsonj pif->pif_sap_before = B_TRUE; 168*3431Scarlsonj dl_addr += dlia->dl_sap_length; 169*3431Scarlsonj } 170*3431Scarlsonj 171*3431Scarlsonj (void) memcpy(pif->pif_hwaddr, dl_addr, pif->pif_hwlen); 172*3431Scarlsonj 173*3431Scarlsonj /* step 5 */ 174*3431Scarlsonj pif->pif_saplen = abs(dlia->dl_sap_length); 175*3431Scarlsonj pif->pif_daddr = build_broadcast_dest(dlia, &pif->pif_dlen); 176*3431Scarlsonj if (pif->pif_daddr == NULL) { 177*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate " 178*3431Scarlsonj "pif_daddr for %s", pname); 179*3431Scarlsonj *error = DHCP_IPC_E_MEMORY; 180*3431Scarlsonj goto failure; 181*3431Scarlsonj } 182*3431Scarlsonj 183*3431Scarlsonj /* Close the DLPI stream until actually needed */ 184*3431Scarlsonj close_dlpi_pif(pif); 185*3431Scarlsonj } 186*3431Scarlsonj 1870Sstevel@tonic-gate /* 188*3431Scarlsonj * This is a bit gross, but IP has a confused interface. We must 189*3431Scarlsonj * assume that the zeroth LIF is plumbed, and must query there to get 190*3431Scarlsonj * the interface index number. 1910Sstevel@tonic-gate */ 192*3431Scarlsonj (void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ); 1930Sstevel@tonic-gate 194*3431Scarlsonj if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFINDEX, &lifr) == -1) { 195*3431Scarlsonj if (errno == ENXIO) 196*3431Scarlsonj *error = DHCP_IPC_E_INVIF; 197*3431Scarlsonj else 198*3431Scarlsonj *error = DHCP_IPC_E_INT; 199*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname); 200*3431Scarlsonj goto failure; 201*3431Scarlsonj } 202*3431Scarlsonj pif->pif_index = lifr.lifr_index; 203*3431Scarlsonj 204*3431Scarlsonj insque(pif, isv6 ? &v6root : &v4root); 205*3431Scarlsonj 206*3431Scarlsonj return (pif); 207*3431Scarlsonj 208*3431Scarlsonj failure: 209*3431Scarlsonj release_pif(pif); 210*3431Scarlsonj return (NULL); 211*3431Scarlsonj } 212*3431Scarlsonj 213*3431Scarlsonj /* 214*3431Scarlsonj * hold_pif(): acquire a hold on a physical interface structure. 215*3431Scarlsonj * 216*3431Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure 217*3431Scarlsonj * output: none 218*3431Scarlsonj */ 219*3431Scarlsonj 220*3431Scarlsonj void 221*3431Scarlsonj hold_pif(dhcp_pif_t *pif) 222*3431Scarlsonj { 223*3431Scarlsonj pif->pif_hold_count++; 224*3431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name, 225*3431Scarlsonj pif->pif_hold_count); 226*3431Scarlsonj } 227*3431Scarlsonj 228*3431Scarlsonj /* 229*3431Scarlsonj * release_pif(): release a hold on a physical interface structure; will 230*3431Scarlsonj * destroy the structure on the last hold removed. 231*3431Scarlsonj * 232*3431Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure 233*3431Scarlsonj * output: none 234*3431Scarlsonj */ 235*3431Scarlsonj 236*3431Scarlsonj void 237*3431Scarlsonj release_pif(dhcp_pif_t *pif) 238*3431Scarlsonj { 239*3431Scarlsonj if (pif->pif_hold_count == 0) { 240*3431Scarlsonj dhcpmsg(MSG_CRIT, "release_pif: extraneous release"); 241*3431Scarlsonj return; 242*3431Scarlsonj } 243*3431Scarlsonj 244*3431Scarlsonj if (--pif->pif_hold_count == 0) { 245*3431Scarlsonj dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s", 246*3431Scarlsonj pif->pif_name); 247*3431Scarlsonj 248*3431Scarlsonj remque(pif); 249*3431Scarlsonj pif->pif_dlpi_count = 1; 250*3431Scarlsonj close_dlpi_pif(pif); 251*3431Scarlsonj free(pif->pif_hwaddr); 252*3431Scarlsonj free(pif->pif_daddr); 253*3431Scarlsonj free(pif); 254*3431Scarlsonj } else { 255*3431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u", 256*3431Scarlsonj pif->pif_name, pif->pif_hold_count); 257*3431Scarlsonj } 258*3431Scarlsonj } 259*3431Scarlsonj 260*3431Scarlsonj /* 261*3431Scarlsonj * lookup_pif_by_index(): Looks up PIF entries given regular ifIndex. 262*3431Scarlsonj * 263*3431Scarlsonj * input: uint_t: the interface index 264*3431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 265*3431Scarlsonj * output: dhcp_pif_t *: the matching PIF, or NULL if not found 266*3431Scarlsonj */ 267*3431Scarlsonj 268*3431Scarlsonj dhcp_pif_t * 269*3431Scarlsonj lookup_pif_by_index(uint_t ifindex, boolean_t isv6) 270*3431Scarlsonj { 271*3431Scarlsonj dhcp_pif_t *pif; 272*3431Scarlsonj 273*3431Scarlsonj for (pif = isv6 ? v6root : v4root; pif != NULL; pif = pif->pif_next) { 274*3431Scarlsonj if (pif->pif_index == ifindex) 275*3431Scarlsonj break; 276*3431Scarlsonj } 277*3431Scarlsonj 278*3431Scarlsonj return (pif); 279*3431Scarlsonj } 280*3431Scarlsonj 281*3431Scarlsonj /* 282*3431Scarlsonj * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and 283*3431Scarlsonj * previous PIF pointer (or NULL for list start). 284*3431Scarlsonj * Caller is expected to iterate through all 285*3431Scarlsonj * potential matches to find interface of interest. 286*3431Scarlsonj * 287*3431Scarlsonj * input: uint16_t: the interface index (truncated) 288*3431Scarlsonj * dhcp_pif_t *: the previous PIF, or NULL for list start 289*3431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 290*3431Scarlsonj * output: dhcp_pif_t *: the next matching PIF, or NULL if not found 291*3431Scarlsonj * note: This operates using the 'truncated' (16-bit) ifindex as seen by 292*3431Scarlsonj * routing socket clients. The value stored in pif_index is the 293*3431Scarlsonj * 32-bit ifindex from the ioctl interface. 294*3431Scarlsonj */ 295*3431Scarlsonj 296*3431Scarlsonj dhcp_pif_t * 297*3431Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6) 298*3431Scarlsonj { 299*3431Scarlsonj if (pif == NULL) 300*3431Scarlsonj pif = isv6 ? v6root : v4root; 301*3431Scarlsonj else 302*3431Scarlsonj pif = pif->pif_next; 303*3431Scarlsonj 304*3431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 305*3431Scarlsonj if ((pif->pif_index & 0xffff) == ifindex) 306*3431Scarlsonj break; 307*3431Scarlsonj } 308*3431Scarlsonj 309*3431Scarlsonj return (pif); 310*3431Scarlsonj } 311*3431Scarlsonj 312*3431Scarlsonj /* 313*3431Scarlsonj * lookup_pif_by_name(): Looks up a physical interface entry given a name. 314*3431Scarlsonj * 315*3431Scarlsonj * input: const char *: the physical interface name 316*3431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 317*3431Scarlsonj * output: dhcp_pif_t *: the matching PIF, or NULL if not found 318*3431Scarlsonj */ 319*3431Scarlsonj 320*3431Scarlsonj dhcp_pif_t * 321*3431Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6) 322*3431Scarlsonj { 323*3431Scarlsonj dhcp_pif_t *pif; 324*3431Scarlsonj 325*3431Scarlsonj pif = isv6 ? v6root : v4root; 326*3431Scarlsonj 327*3431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 328*3431Scarlsonj if (strcmp(pif->pif_name, pname) == 0) 329*3431Scarlsonj break; 330*3431Scarlsonj } 331*3431Scarlsonj 332*3431Scarlsonj return (pif); 333*3431Scarlsonj } 334*3431Scarlsonj 335*3431Scarlsonj /* 336*3431Scarlsonj * open_dlpi_pif(): register the use of DLPI I/O by a LIF on a PIF, opening 337*3431Scarlsonj * the connection if necessary. 338*3431Scarlsonj * 339*3431Scarlsonj * input: dhcp_pif_t *: the physical interface on which to use DLPI 340*3431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure. 341*3431Scarlsonj */ 342*3431Scarlsonj 343*3431Scarlsonj boolean_t 344*3431Scarlsonj open_dlpi_pif(dhcp_pif_t *pif) 345*3431Scarlsonj { 346*3431Scarlsonj if (pif->pif_dlpi_fd == -1) { 347*3431Scarlsonj uint32_t buf[DLPI_BUF_MAX / sizeof (uint32_t)]; 348*3431Scarlsonj dl_info_ack_t *dlia = (dl_info_ack_t *)buf; 349*3431Scarlsonj 350*3431Scarlsonj pif->pif_dlpi_fd = dlpi_open(pif->pif_name, dlia, sizeof (buf), 351*3431Scarlsonj ETHERTYPE_IP); 352*3431Scarlsonj if (pif->pif_dlpi_fd == -1) 353*3431Scarlsonj return (B_FALSE); 354*3431Scarlsonj set_packet_filter(pif->pif_dlpi_fd, dhcp_filter, NULL, "DHCP"); 355*3431Scarlsonj pif->pif_dlpi_id = iu_register_event(eh, pif->pif_dlpi_fd, 356*3431Scarlsonj POLLIN, dhcp_collect_dlpi, pif); 357*3431Scarlsonj if (pif->pif_dlpi_id == -1) { 358*3431Scarlsonj (void) dlpi_close(pif->pif_dlpi_fd); 359*3431Scarlsonj pif->pif_dlpi_fd = -1; 360*3431Scarlsonj return (B_FALSE); 361*3431Scarlsonj } 362*3431Scarlsonj } 363*3431Scarlsonj pif->pif_dlpi_count++; 364*3431Scarlsonj return (B_TRUE); 365*3431Scarlsonj } 366*3431Scarlsonj 367*3431Scarlsonj /* 368*3431Scarlsonj * close_dlpi_pif(): unregister the use of DLPI I/O by a LIF on a PIF, closing 369*3431Scarlsonj * the connection if this was the last user. 370*3431Scarlsonj * 371*3431Scarlsonj * input: dhcp_pif_t *: the physical interface on which we're using DLPI 372*3431Scarlsonj * output: none 373*3431Scarlsonj */ 374*3431Scarlsonj 375*3431Scarlsonj void 376*3431Scarlsonj close_dlpi_pif(dhcp_pif_t *pif) 377*3431Scarlsonj { 378*3431Scarlsonj if (pif->pif_dlpi_count > 1) { 379*3431Scarlsonj pif->pif_dlpi_count--; 380*3431Scarlsonj return; 381*3431Scarlsonj } 382*3431Scarlsonj pif->pif_dlpi_count = 0; 383*3431Scarlsonj if (pif->pif_dlpi_id != -1) { 384*3431Scarlsonj (void) iu_unregister_event(eh, pif->pif_dlpi_id, NULL); 385*3431Scarlsonj pif->pif_dlpi_id = -1; 386*3431Scarlsonj } 387*3431Scarlsonj if (pif->pif_dlpi_fd != -1) { 388*3431Scarlsonj (void) dlpi_close(pif->pif_dlpi_fd); 389*3431Scarlsonj pif->pif_dlpi_fd = -1; 390*3431Scarlsonj } 391*3431Scarlsonj } 392*3431Scarlsonj 393*3431Scarlsonj /* 394*3431Scarlsonj * pif_status(): update the physical interface up/down status. 395*3431Scarlsonj * 396*3431Scarlsonj * input: dhcp_pif_t *: the physical interface on which we're using DLPI 397*3431Scarlsonj * boolean_t: B_TRUE if the interface is going up 398*3431Scarlsonj * output: none 399*3431Scarlsonj */ 400*3431Scarlsonj 401*3431Scarlsonj void 402*3431Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup) 403*3431Scarlsonj { 404*3431Scarlsonj dhcp_lif_t *lif; 405*3431Scarlsonj dhcp_smach_t *dsmp; 406*3431Scarlsonj 407*3431Scarlsonj pif->pif_running = isup; 408*3431Scarlsonj dhcpmsg(LOG_DEBUG, "interface %s has %s", pif->pif_name, 409*3431Scarlsonj isup ? "come back up" : "gone down"); 410*3431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 411*3431Scarlsonj for (dsmp = lif->lif_smachs; dsmp != NULL; 412*3431Scarlsonj dsmp = dsmp->dsm_next) { 413*3431Scarlsonj if (isup) 414*3431Scarlsonj refresh_smach(dsmp); 415*3431Scarlsonj else 416*3431Scarlsonj remove_default_routes(dsmp); 417*3431Scarlsonj } 418*3431Scarlsonj } 419*3431Scarlsonj } 420*3431Scarlsonj 421*3431Scarlsonj /* Helper for insert_lif: extract addresses as defined */ 422*3431Scarlsonj #define ASSIGN_ADDR(v4, v6, lf) \ 423*3431Scarlsonj if (pif->pif_isv6) { \ 424*3431Scarlsonj lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \ 425*3431Scarlsonj } else { \ 426*3431Scarlsonj lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \ 427*3431Scarlsonj } 428*3431Scarlsonj 429*3431Scarlsonj /* 430*3431Scarlsonj * insert_lif(): Creates a new logical interface structure and chains it on 431*3431Scarlsonj * the list for a given physical interface. Initializes state 432*3431Scarlsonj * that remains consistent across all use of the logical 433*3431Scarlsonj * interface entry. Caller's PIF hold is transferred to the 434*3431Scarlsonj * LIF on success, and is dropped on failure. 435*3431Scarlsonj * 436*3431Scarlsonj * input: dhcp_pif_t *: pointer to the physical interface for this LIF 437*3431Scarlsonj * const char *: the name of the logical interface 438*3431Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 439*3431Scarlsonj * error code with the reason why 440*3431Scarlsonj * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure 441*3431Scarlsonj */ 442*3431Scarlsonj 443*3431Scarlsonj dhcp_lif_t * 444*3431Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error) 445*3431Scarlsonj { 446*3431Scarlsonj dhcp_lif_t *lif; 447*3431Scarlsonj int fd; 448*3431Scarlsonj struct lifreq lifr; 449*3431Scarlsonj 450*3431Scarlsonj if ((lif = calloc(1, sizeof (*lif))) == NULL) { 451*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for " 452*3431Scarlsonj "%s", lname); 4530Sstevel@tonic-gate *error = DHCP_IPC_E_MEMORY; 4540Sstevel@tonic-gate return (NULL); 4550Sstevel@tonic-gate } 4560Sstevel@tonic-gate 457*3431Scarlsonj lif->lif_sock_ip_fd = -1; 458*3431Scarlsonj lif->lif_acknak_id = -1; 459*3431Scarlsonj lif->lif_iaid_id = -1; 460*3431Scarlsonj lif->lif_hold_count = 1; 461*3431Scarlsonj lif->lif_pif = pif; 462*3431Scarlsonj lif->lif_removed = B_TRUE; 463*3431Scarlsonj init_timer(&lif->lif_preferred, 0); 464*3431Scarlsonj init_timer(&lif->lif_expire, 0); 4650Sstevel@tonic-gate 466*3431Scarlsonj if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) { 467*3431Scarlsonj dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long", 468*3431Scarlsonj lname); 4690Sstevel@tonic-gate *error = DHCP_IPC_E_INVIF; 4700Sstevel@tonic-gate goto failure; 4710Sstevel@tonic-gate } 4720Sstevel@tonic-gate 473*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ); 474*3431Scarlsonj 475*3431Scarlsonj fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 4760Sstevel@tonic-gate 477*3431Scarlsonj if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) 478*3431Scarlsonj lif->lif_max = 1024; 479*3431Scarlsonj else 480*3431Scarlsonj lif->lif_max = lifr.lifr_mtu; 4810Sstevel@tonic-gate 482*3431Scarlsonj if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) { 483*3431Scarlsonj if (errno == ENXIO) 484*3431Scarlsonj *error = DHCP_IPC_E_INVIF; 485*3431Scarlsonj else 486*3431Scarlsonj *error = DHCP_IPC_E_INT; 487*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname); 4880Sstevel@tonic-gate goto failure; 4890Sstevel@tonic-gate } 490*3431Scarlsonj ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr); 4910Sstevel@tonic-gate 492*3431Scarlsonj if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 4932546Scarlsonj if (errno == ENXIO) 4942546Scarlsonj *error = DHCP_IPC_E_INVIF; 4952546Scarlsonj else 4962546Scarlsonj *error = DHCP_IPC_E_INT; 497*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname); 498*3431Scarlsonj goto failure; 499*3431Scarlsonj } 500*3431Scarlsonj ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr); 501*3431Scarlsonj 502*3431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 503*3431Scarlsonj *error = DHCP_IPC_E_INT; 504*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname); 5052546Scarlsonj goto failure; 5062546Scarlsonj } 507*3431Scarlsonj lif->lif_flags = lifr.lifr_flags; 508*3431Scarlsonj 509*3431Scarlsonj /* 510*3431Scarlsonj * If we've just detected the interface going up or down, then signal 511*3431Scarlsonj * an appropriate action. There may be other state machines here. 512*3431Scarlsonj */ 513*3431Scarlsonj if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) { 514*3431Scarlsonj pif_status(pif, B_TRUE); 515*3431Scarlsonj } else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) { 516*3431Scarlsonj pif_status(pif, B_FALSE); 517*3431Scarlsonj } 518*3431Scarlsonj 519*3431Scarlsonj if (lifr.lifr_flags & IFF_POINTOPOINT) { 520*3431Scarlsonj if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) { 521*3431Scarlsonj *error = DHCP_IPC_E_INT; 522*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s", 523*3431Scarlsonj lname); 524*3431Scarlsonj goto failure; 525*3431Scarlsonj } 526*3431Scarlsonj ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr); 527*3431Scarlsonj } else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) { 528*3431Scarlsonj if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) { 529*3431Scarlsonj *error = DHCP_IPC_E_INT; 530*3431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s", 531*3431Scarlsonj lname); 532*3431Scarlsonj goto failure; 533*3431Scarlsonj } 534*3431Scarlsonj lif->lif_broadcast = 535*3431Scarlsonj ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr. 536*3431Scarlsonj s_addr; 537*3431Scarlsonj } 538*3431Scarlsonj 539*3431Scarlsonj if (pif->pif_isv6) 540*3431Scarlsonj cached_v6_max_mtu = 0; 541*3431Scarlsonj else 542*3431Scarlsonj cached_v4_max_mtu = 0; 543*3431Scarlsonj 544*3431Scarlsonj lif->lif_removed = B_FALSE; 545*3431Scarlsonj insque(lif, &pif->pif_lifs); 546*3431Scarlsonj 547*3431Scarlsonj return (lif); 548*3431Scarlsonj 549*3431Scarlsonj failure: 550*3431Scarlsonj release_lif(lif); 551*3431Scarlsonj return (NULL); 552*3431Scarlsonj } 553*3431Scarlsonj 554*3431Scarlsonj /* 555*3431Scarlsonj * hold_lif(): acquire a hold on a logical interface structure. 556*3431Scarlsonj * 557*3431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 558*3431Scarlsonj * output: none 559*3431Scarlsonj */ 560*3431Scarlsonj 561*3431Scarlsonj void 562*3431Scarlsonj hold_lif(dhcp_lif_t *lif) 563*3431Scarlsonj { 564*3431Scarlsonj lif->lif_hold_count++; 565*3431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name, 566*3431Scarlsonj lif->lif_hold_count); 567*3431Scarlsonj } 568*3431Scarlsonj 569*3431Scarlsonj /* 570*3431Scarlsonj * release_lif(): release a hold on a logical interface structure; will 571*3431Scarlsonj * destroy the structure on the last hold removed. 572*3431Scarlsonj * 573*3431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 574*3431Scarlsonj * output: none 575*3431Scarlsonj */ 576*3431Scarlsonj 577*3431Scarlsonj void 578*3431Scarlsonj release_lif(dhcp_lif_t *lif) 579*3431Scarlsonj { 580*3431Scarlsonj if (lif->lif_hold_count == 0) { 581*3431Scarlsonj dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s", 582*3431Scarlsonj lif->lif_name); 583*3431Scarlsonj return; 584*3431Scarlsonj } 585*3431Scarlsonj 586*3431Scarlsonj if (lif->lif_hold_count == 1 && !lif->lif_removed) { 587*3431Scarlsonj unplumb_lif(lif); 588*3431Scarlsonj return; 589*3431Scarlsonj } 590*3431Scarlsonj 591*3431Scarlsonj if (--lif->lif_hold_count == 0) { 592*3431Scarlsonj dhcp_pif_t *pif; 593*3431Scarlsonj 594*3431Scarlsonj dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s", 595*3431Scarlsonj lif->lif_name); 596*3431Scarlsonj 597*3431Scarlsonj if (lif->lif_lease != NULL) 598*3431Scarlsonj dhcpmsg(MSG_CRIT, 599*3431Scarlsonj "release_lif: still holding lease at last hold!"); 600*3431Scarlsonj close_ip_lif(lif); 601*3431Scarlsonj pif = lif->lif_pif; 602*3431Scarlsonj if (pif->pif_isv6) 603*3431Scarlsonj cached_v6_max_mtu = 0; 604*3431Scarlsonj else 605*3431Scarlsonj cached_v4_max_mtu = 0; 606*3431Scarlsonj release_pif(pif); 607*3431Scarlsonj free(lif); 608*3431Scarlsonj } else { 609*3431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u", 610*3431Scarlsonj lif->lif_name, lif->lif_hold_count); 611*3431Scarlsonj } 612*3431Scarlsonj } 613*3431Scarlsonj 614*3431Scarlsonj /* 615*3431Scarlsonj * remove_lif(): remove a logical interface from its PIF and lease (if any) and 616*3431Scarlsonj * the lease's hold on the LIF. Assumes that we did not plumb 617*3431Scarlsonj * the interface. 618*3431Scarlsonj * 619*3431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 620*3431Scarlsonj * output: none 621*3431Scarlsonj */ 622*3431Scarlsonj 623*3431Scarlsonj void 624*3431Scarlsonj remove_lif(dhcp_lif_t *lif) 625*3431Scarlsonj { 626*3431Scarlsonj if (lif->lif_plumbed) { 627*3431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s", 628*3431Scarlsonj lif->lif_name); 629*3431Scarlsonj return; 630*3431Scarlsonj } 631*3431Scarlsonj if (lif->lif_removed) { 632*3431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s", 633*3431Scarlsonj lif->lif_name); 634*3431Scarlsonj } else { 635*3431Scarlsonj dhcp_lif_t *lifnext; 636*3431Scarlsonj dhcp_lease_t *dlp; 637*3431Scarlsonj 638*3431Scarlsonj dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name); 639*3431Scarlsonj lif->lif_removed = B_TRUE; 640*3431Scarlsonj lifnext = lif->lif_next; 641*3431Scarlsonj clear_lif_dhcp(lif); 642*3431Scarlsonj cancel_lif_timers(lif); 643*3431Scarlsonj if (lif->lif_iaid_id != -1 && 644*3431Scarlsonj iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) { 645*3431Scarlsonj lif->lif_iaid_id = -1; 646*3431Scarlsonj release_lif(lif); 647*3431Scarlsonj } 648*3431Scarlsonj 649*3431Scarlsonj /* Remove from PIF list */ 650*3431Scarlsonj remque(lif); 651*3431Scarlsonj 652*3431Scarlsonj /* If we were part of a lease, then remove ourselves */ 653*3431Scarlsonj if ((dlp = lif->lif_lease) != NULL) { 654*3431Scarlsonj if (--dlp->dl_nlifs == 0) 655*3431Scarlsonj dlp->dl_lifs = NULL; 656*3431Scarlsonj else if (dlp->dl_lifs == lif) 657*3431Scarlsonj dlp->dl_lifs = lifnext; 658*3431Scarlsonj if (lif->lif_flags & IFF_DHCPRUNNING) 659*3431Scarlsonj clear_lif_dhcp(lif); 660*3431Scarlsonj if (lif->lif_declined != NULL) { 661*3431Scarlsonj dlp->dl_smach->dsm_lif_down--; 662*3431Scarlsonj lif->lif_declined = NULL; 663*3431Scarlsonj } 664*3431Scarlsonj lif->lif_lease = NULL; 665*3431Scarlsonj release_lif(lif); 666*3431Scarlsonj } 667*3431Scarlsonj } 668*3431Scarlsonj } 669*3431Scarlsonj 670*3431Scarlsonj /* 671*3431Scarlsonj * lookup_lif_by_name(): Looks up a logical interface entry given a name and 672*3431Scarlsonj * a physical interface. 673*3431Scarlsonj * 674*3431Scarlsonj * input: const char *: the logical interface name 675*3431Scarlsonj * const dhcp_pif_t *: the physical interface 676*3431Scarlsonj * output: dhcp_lif_t *: the matching LIF, or NULL if not found 677*3431Scarlsonj */ 678*3431Scarlsonj 679*3431Scarlsonj dhcp_lif_t * 680*3431Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif) 681*3431Scarlsonj { 682*3431Scarlsonj dhcp_lif_t *lif; 683*3431Scarlsonj 684*3431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 685*3431Scarlsonj if (strcmp(lif->lif_name, lname) == 0) 686*3431Scarlsonj break; 687*3431Scarlsonj } 688*3431Scarlsonj 689*3431Scarlsonj return (lif); 690*3431Scarlsonj } 691*3431Scarlsonj 692*3431Scarlsonj /* 693*3431Scarlsonj * checkaddr(): checks if the given address is still set on the given LIF 694*3431Scarlsonj * 695*3431Scarlsonj * input: const dhcp_lif_t *: the LIF to check 696*3431Scarlsonj * int: the address to look up on the interface (ioctl) 697*3431Scarlsonj * const in6_addr_t *: the address to compare to 698*3431Scarlsonj * const char *: name of the address for logging purposes 699*3431Scarlsonj * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not 700*3431Scarlsonj */ 701*3431Scarlsonj 702*3431Scarlsonj static boolean_t 703*3431Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr, 704*3431Scarlsonj const char *aname) 705*3431Scarlsonj { 706*3431Scarlsonj boolean_t isv6; 707*3431Scarlsonj int fd; 708*3431Scarlsonj struct lifreq lifr; 709*3431Scarlsonj 710*3431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 711*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 712*3431Scarlsonj 713*3431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 714*3431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 715*3431Scarlsonj 716*3431Scarlsonj if (ioctl(fd, ioccmd, &lifr) == -1) { 717*3431Scarlsonj if (errno == ENXIO) { 718*3431Scarlsonj dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone", 719*3431Scarlsonj lif->lif_name); 720*3431Scarlsonj return (B_FALSE); 721*3431Scarlsonj } 722*3431Scarlsonj dhcpmsg(MSG_DEBUG, 723*3431Scarlsonj "checkaddr: ignoring ioctl error on %s %x: %s", 724*3431Scarlsonj lif->lif_name, ioccmd, strerror(errno)); 725*3431Scarlsonj } else if (isv6) { 726*3431Scarlsonj struct sockaddr_in6 *sin6 = 727*3431Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr; 728*3431Scarlsonj char abuf1[INET6_ADDRSTRLEN]; 729*3431Scarlsonj char abuf2[INET6_ADDRSTRLEN]; 730*3431Scarlsonj 731*3431Scarlsonj if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) { 732*3431Scarlsonj dhcpmsg(MSG_WARNING, 733*3431Scarlsonj "checkaddr: expected %s %s on %s, have %s", 734*3431Scarlsonj aname, inet_ntop(AF_INET6, &sin6->sin6_addr, abuf1, 735*3431Scarlsonj sizeof (abuf1)), lif->lif_name, 736*3431Scarlsonj inet_ntop(AF_INET6, addr, abuf2, sizeof (abuf2))); 737*3431Scarlsonj return (B_FALSE); 738*3431Scarlsonj } 739*3431Scarlsonj } else { 740*3431Scarlsonj struct sockaddr_in *sinp = 741*3431Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr; 742*3431Scarlsonj ipaddr_t v4addr; 743*3431Scarlsonj char abuf1[INET_ADDRSTRLEN]; 744*3431Scarlsonj char abuf2[INET_ADDRSTRLEN]; 745*3431Scarlsonj 746*3431Scarlsonj IN6_V4MAPPED_TO_IPADDR(addr, v4addr); 747*3431Scarlsonj if (sinp->sin_addr.s_addr != v4addr) { 748*3431Scarlsonj dhcpmsg(MSG_WARNING, 749*3431Scarlsonj "checkaddr: expected %s %s on %s, have %s", 750*3431Scarlsonj aname, inet_ntop(AF_INET, &sinp->sin_addr, abuf1, 751*3431Scarlsonj sizeof (abuf1)), lif->lif_name, 752*3431Scarlsonj inet_ntop(AF_INET, &v4addr, abuf2, 753*3431Scarlsonj sizeof (abuf2))); 754*3431Scarlsonj return (B_FALSE); 755*3431Scarlsonj } 756*3431Scarlsonj } 757*3431Scarlsonj return (B_TRUE); 758*3431Scarlsonj } 759*3431Scarlsonj 760*3431Scarlsonj /* 761*3431Scarlsonj * verify_lif(): verifies than a LIF is still valid (i.e., has not been 762*3431Scarlsonj * explicitly or implicitly dropped or released) 763*3431Scarlsonj * 764*3431Scarlsonj * input: const dhcp_lif_t *: the LIF to verify 765*3431Scarlsonj * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise 766*3431Scarlsonj */ 767*3431Scarlsonj 768*3431Scarlsonj boolean_t 769*3431Scarlsonj verify_lif(const dhcp_lif_t *lif) 770*3431Scarlsonj { 771*3431Scarlsonj boolean_t isv6; 772*3431Scarlsonj int fd; 773*3431Scarlsonj struct lifreq lifr; 774*3431Scarlsonj 775*3431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 776*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 777*3431Scarlsonj 778*3431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 779*3431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 780*3431Scarlsonj 781*3431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 782*3431Scarlsonj dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFFLAGS failed on %s", 783*3431Scarlsonj lif->lif_name); 784*3431Scarlsonj return (B_FALSE); 785*3431Scarlsonj } 786*3431Scarlsonj 787*3431Scarlsonj /* 788*3431Scarlsonj * If important flags have changed, then abandon the interface. 789*3431Scarlsonj */ 790*3431Scarlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) { 791*3431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: " 792*3431Scarlsonj "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags, 793*3431Scarlsonj lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) & 794*3431Scarlsonj DHCP_IFF_WATCH); 795*3431Scarlsonj return (B_FALSE); 796*3431Scarlsonj } 797*3431Scarlsonj 798*3431Scarlsonj /* 799*3431Scarlsonj * Special case: if the interface has gone down as a duplicate, then 800*3431Scarlsonj * this alone does _not_ mean that we're abandoning it just yet. Allow 801*3431Scarlsonj * the state machine to handle this normally by trying to get a new 802*3431Scarlsonj * lease. 803*3431Scarlsonj */ 804*3431Scarlsonj if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) { 805*3431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s", 806*3431Scarlsonj lif->lif_name); 807*3431Scarlsonj return (B_TRUE); 808*3431Scarlsonj } 809*3431Scarlsonj 810*3431Scarlsonj /* 811*3431Scarlsonj * If the user has torn down or started up the interface manually, then 812*3431Scarlsonj * abandon the lease. 813*3431Scarlsonj */ 814*3431Scarlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) { 815*3431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s", 816*3431Scarlsonj lifr.lifr_flags & IFF_UP ? "started up" : "shut down", 817*3431Scarlsonj lif->lif_name); 818*3431Scarlsonj return (B_FALSE); 819*3431Scarlsonj } 820*3431Scarlsonj 821*3431Scarlsonj /* 822*3431Scarlsonj * Check for delete and recreate. 823*3431Scarlsonj */ 824*3431Scarlsonj if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 825*3431Scarlsonj dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s", 826*3431Scarlsonj lif->lif_name); 827*3431Scarlsonj return (B_FALSE); 828*3431Scarlsonj } 829*3431Scarlsonj if (lifr.lifr_index != lif->lif_pif->pif_index) { 830*3431Scarlsonj dhcpmsg(MSG_DEBUG, 831*3431Scarlsonj "verify_lif: ifindex on %s changed: %u to %u", 832*3431Scarlsonj lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index); 833*3431Scarlsonj return (B_FALSE); 834*3431Scarlsonj } 835*3431Scarlsonj 836*3431Scarlsonj /* 837*3431Scarlsonj * If the IP address, netmask, or broadcast address have changed, or 838*3431Scarlsonj * the interface has been unplumbed, then we act like there has been an 839*3431Scarlsonj * implicit drop. (Note that the netmask is under DHCP control for 840*3431Scarlsonj * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast 841*3431Scarlsonj * addresses.) 842*3431Scarlsonj */ 8432546Scarlsonj 844*3431Scarlsonj if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address")) 845*3431Scarlsonj return (B_FALSE); 846*3431Scarlsonj 847*3431Scarlsonj if (isv6) { 848*3431Scarlsonj /* 849*3431Scarlsonj * If it's not point-to-point, we're done. If it is, then 850*3431Scarlsonj * check the peer's address as well. 851*3431Scarlsonj */ 852*3431Scarlsonj return (!(lif->lif_flags & IFF_POINTOPOINT) || 853*3431Scarlsonj checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer, 854*3431Scarlsonj "peer address")); 855*3431Scarlsonj } else { 856*3431Scarlsonj if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask, 857*3431Scarlsonj "netmask")) 858*3431Scarlsonj return (B_FALSE); 859*3431Scarlsonj 860*3431Scarlsonj return (checkaddr(lif, 861*3431Scarlsonj (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR : 862*3431Scarlsonj SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address")); 863*3431Scarlsonj } 864*3431Scarlsonj } 865*3431Scarlsonj 866*3431Scarlsonj /* 867*3431Scarlsonj * canonize_lif(): puts the interface in a canonical (zeroed) form. This is 868*3431Scarlsonj * used only on the "main" LIF for IPv4. All other interfaces 869*3431Scarlsonj * are under dhcpagent control and are removed using 870*3431Scarlsonj * unplumb_lif(). 871*3431Scarlsonj * 872*3431Scarlsonj * input: dhcp_lif_t *: the interface to canonize 873*3431Scarlsonj * output: none 874*3431Scarlsonj */ 875*3431Scarlsonj 876*3431Scarlsonj static void 877*3431Scarlsonj canonize_lif(dhcp_lif_t *lif) 878*3431Scarlsonj { 879*3431Scarlsonj boolean_t isv6; 880*3431Scarlsonj int fd; 881*3431Scarlsonj struct lifreq lifr; 882*3431Scarlsonj 883*3431Scarlsonj /* 884*3431Scarlsonj * If there's nothing here, then don't touch the interface. This can 885*3431Scarlsonj * happen when an already-canonized LIF is recanonized. 886*3431Scarlsonj */ 887*3431Scarlsonj if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr)) 888*3431Scarlsonj return; 889*3431Scarlsonj 890*3431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 891*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s", 892*3431Scarlsonj isv6 ? 6 : 4, lif->lif_name); 893*3431Scarlsonj 894*3431Scarlsonj lif->lif_v6addr = my_in6addr_any; 895*3431Scarlsonj lif->lif_v6mask = my_in6addr_any; 896*3431Scarlsonj lif->lif_v6peer = my_in6addr_any; 897*3431Scarlsonj 898*3431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 899*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 900*3431Scarlsonj 901*3431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 902*3431Scarlsonj 903*3431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 904*3431Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s", 905*3431Scarlsonj lif->lif_name); 906*3431Scarlsonj return; 907*3431Scarlsonj } 908*3431Scarlsonj 909*3431Scarlsonj /* Should not happen */ 910*3431Scarlsonj if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) { 911*3431Scarlsonj dhcpmsg(MSG_INFO, 912*3431Scarlsonj "canonize_lif: cannot clear %s; flags are %llx", 913*3431Scarlsonj lif->lif_name, lifr.lifr_flags); 914*3431Scarlsonj return; 915*3431Scarlsonj } 916*3431Scarlsonj 917*3431Scarlsonj /* 918*3431Scarlsonj * clear the UP flag, but don't clear DHCPRUNNING since 919*3431Scarlsonj * that should only be done when the interface is removed 920*3431Scarlsonj * (see clear_lif_dhcp() and remove_lif()) 921*3431Scarlsonj */ 922*3431Scarlsonj 923*3431Scarlsonj lif->lif_flags = lifr.lifr_flags &= ~IFF_UP; 924*3431Scarlsonj 925*3431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 926*3431Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't set flags for %s", 927*3431Scarlsonj lif->lif_name); 928*3431Scarlsonj return; 929*3431Scarlsonj } 930*3431Scarlsonj 931*3431Scarlsonj (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); 932*3431Scarlsonj if (isv6) { 933*3431Scarlsonj struct sockaddr_in6 *sin6 = 934*3431Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr; 935*3431Scarlsonj 936*3431Scarlsonj sin6->sin6_family = AF_INET6; 937*3431Scarlsonj sin6->sin6_addr = my_in6addr_any; 938*3431Scarlsonj } else { 939*3431Scarlsonj struct sockaddr_in *sinv = 940*3431Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr; 941*3431Scarlsonj 942*3431Scarlsonj sinv->sin_family = AF_INET; 943*3431Scarlsonj sinv->sin_addr.s_addr = htonl(INADDR_ANY); 944*3431Scarlsonj } 945*3431Scarlsonj 946*3431Scarlsonj if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) { 947*3431Scarlsonj dhcpmsg(MSG_ERR, 948*3431Scarlsonj "canonize_lif: can't clear local address on %s", 949*3431Scarlsonj lif->lif_name); 950*3431Scarlsonj } 951*3431Scarlsonj 952*3431Scarlsonj /* Netmask is under in.ndpd control with IPv6 */ 953*3431Scarlsonj if (!isv6 && ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) { 954*3431Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't clear netmask on %s", 955*3431Scarlsonj lif->lif_name); 956*3431Scarlsonj } 957*3431Scarlsonj 958*3431Scarlsonj if (lif->lif_flags & IFF_POINTOPOINT) { 959*3431Scarlsonj if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) { 960*3431Scarlsonj dhcpmsg(MSG_ERR, 961*3431Scarlsonj "canonize_lif: can't clear remote address on %s", 962*3431Scarlsonj lif->lif_name); 963*3431Scarlsonj } 964*3431Scarlsonj } else if (!isv6) { 965*3431Scarlsonj if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) { 966*3431Scarlsonj dhcpmsg(MSG_ERR, 967*3431Scarlsonj "canonize_lif: can't clear broadcast address on %s", 968*3431Scarlsonj lif->lif_name); 969*3431Scarlsonj } 970*3431Scarlsonj } 971*3431Scarlsonj } 972*3431Scarlsonj 973*3431Scarlsonj /* 974*3431Scarlsonj * plumb_lif(): Adds the LIF to the system. This is used for all 975*3431Scarlsonj * DHCPv6-derived interfaces. The returned LIF has a hold 976*3431Scarlsonj * on it. 977*3431Scarlsonj * 978*3431Scarlsonj * input: dhcp_lif_t *: the interface to unplumb 979*3431Scarlsonj * output: none 980*3431Scarlsonj */ 981*3431Scarlsonj 982*3431Scarlsonj dhcp_lif_t * 983*3431Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr) 984*3431Scarlsonj { 985*3431Scarlsonj dhcp_lif_t *lif; 986*3431Scarlsonj char abuf[INET6_ADDRSTRLEN]; 987*3431Scarlsonj struct lifreq lifr; 988*3431Scarlsonj struct sockaddr_in6 *sin6; 989*3431Scarlsonj int error; 990*3431Scarlsonj 991*3431Scarlsonj (void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)); 992*3431Scarlsonj 993*3431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 994*3431Scarlsonj if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) { 995*3431Scarlsonj dhcpmsg(MSG_ERR, 996*3431Scarlsonj "plumb_lif: entry for %s already exists!", abuf); 997*3431Scarlsonj return (NULL); 998*3431Scarlsonj } 999*3431Scarlsonj } 1000*3431Scarlsonj 1001*3431Scarlsonj /* First, create a new zero-address logical interface */ 1002*3431Scarlsonj (void) memset(&lifr, 0, sizeof (lifr)); 1003*3431Scarlsonj (void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name)); 1004*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) { 1005*3431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name); 1006*3431Scarlsonj return (NULL); 1007*3431Scarlsonj } 1008*3431Scarlsonj 1009*3431Scarlsonj /* Next, set the netmask to all ones */ 1010*3431Scarlsonj sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 1011*3431Scarlsonj sin6->sin6_family = AF_INET6; 1012*3431Scarlsonj (void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr)); 1013*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 1014*3431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s", 1015*3431Scarlsonj lifr.lifr_name); 10160Sstevel@tonic-gate goto failure; 10170Sstevel@tonic-gate } 10180Sstevel@tonic-gate 1019*3431Scarlsonj /* Now set the interface address */ 1020*3431Scarlsonj sin6->sin6_addr = *addr; 1021*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 1022*3431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s", 1023*3431Scarlsonj lifr.lifr_name, abuf); 1024*3431Scarlsonj goto failure; 1025*3431Scarlsonj } 1026*3431Scarlsonj 1027*3431Scarlsonj /* Mark the interface up */ 1028*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 1029*3431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s", 1030*3431Scarlsonj lifr.lifr_name); 1031*3431Scarlsonj goto failure; 1032*3431Scarlsonj } 1033*3431Scarlsonj lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING; 1034*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 1035*3431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s", 1036*3431Scarlsonj lifr.lifr_name); 1037*3431Scarlsonj goto failure; 1038*3431Scarlsonj } 1039*3431Scarlsonj 1040*3431Scarlsonj /* Now we can create the internal LIF structure */ 1041*3431Scarlsonj hold_pif(pif); 1042*3431Scarlsonj if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL) 1043*3431Scarlsonj goto failure; 1044*3431Scarlsonj 1045*3431Scarlsonj dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf, 1046*3431Scarlsonj lif->lif_name); 1047*3431Scarlsonj lif->lif_plumbed = B_TRUE; 1048*3431Scarlsonj 1049*3431Scarlsonj return (lif); 1050*3431Scarlsonj 1051*3431Scarlsonj failure: 1052*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 1053*3431Scarlsonj errno != ENXIO) { 1054*3431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s", 1055*3431Scarlsonj lifr.lifr_name); 1056*3431Scarlsonj } 1057*3431Scarlsonj return (NULL); 1058*3431Scarlsonj } 1059*3431Scarlsonj 1060*3431Scarlsonj /* 1061*3431Scarlsonj * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used 1062*3431Scarlsonj * for all interfaces configured by DHCP (those in leases). 1063*3431Scarlsonj * 1064*3431Scarlsonj * input: dhcp_lif_t *: the interface to unplumb 1065*3431Scarlsonj * output: none 1066*3431Scarlsonj */ 1067*3431Scarlsonj 1068*3431Scarlsonj void 1069*3431Scarlsonj unplumb_lif(dhcp_lif_t *lif) 1070*3431Scarlsonj { 1071*3431Scarlsonj dhcp_lease_t *dlp; 1072*3431Scarlsonj 1073*3431Scarlsonj if (lif->lif_plumbed) { 1074*3431Scarlsonj struct lifreq lifr; 1075*3431Scarlsonj 1076*3431Scarlsonj (void) memset(&lifr, 0, sizeof (lifr)); 1077*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, 1078*3431Scarlsonj sizeof (lifr.lifr_name)); 1079*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 1080*3431Scarlsonj errno != ENXIO) { 1081*3431Scarlsonj dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s", 1082*3431Scarlsonj lif->lif_name); 1083*3431Scarlsonj } 1084*3431Scarlsonj lif->lif_plumbed = B_FALSE; 1085*3431Scarlsonj } 1086*3431Scarlsonj lif->lif_flags = 0; 1087*3431Scarlsonj /* 1088*3431Scarlsonj * Special case: if we're "unplumbing" the main LIF for DHCPv4, then 1089*3431Scarlsonj * just canonize it and remove it from the lease. 1090*3431Scarlsonj */ 1091*3431Scarlsonj if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) { 1092*3431Scarlsonj canonize_lif(lif); 1093*3431Scarlsonj cancel_lif_timers(lif); 1094*3431Scarlsonj if (lif->lif_declined != NULL) { 1095*3431Scarlsonj dlp->dl_smach->dsm_lif_down--; 1096*3431Scarlsonj lif->lif_declined = NULL; 1097*3431Scarlsonj } 1098*3431Scarlsonj dlp->dl_nlifs = 0; 1099*3431Scarlsonj dlp->dl_lifs = NULL; 1100*3431Scarlsonj lif->lif_lease = NULL; 1101*3431Scarlsonj release_lif(lif); 1102*3431Scarlsonj } else { 1103*3431Scarlsonj remove_lif(lif); 1104*3431Scarlsonj } 1105*3431Scarlsonj } 1106*3431Scarlsonj 1107*3431Scarlsonj /* 1108*3431Scarlsonj * attach_lif(): create a new logical interface, creating the physical 1109*3431Scarlsonj * interface as necessary. 1110*3431Scarlsonj * 1111*3431Scarlsonj * input: const char *: the logical interface name 1112*3431Scarlsonj * boolean_t: B_TRUE for IPv6 1113*3431Scarlsonj * int *: set to DHCP_IPC_E_* if creation fails 1114*3431Scarlsonj * output: dhcp_lif_t *: pointer to new entry, or NULL on failure 1115*3431Scarlsonj */ 1116*3431Scarlsonj 1117*3431Scarlsonj dhcp_lif_t * 1118*3431Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error) 1119*3431Scarlsonj { 1120*3431Scarlsonj dhcp_pif_t *pif; 1121*3431Scarlsonj char pname[LIFNAMSIZ], *cp; 1122*3431Scarlsonj 1123*3431Scarlsonj (void) strlcpy(pname, lname, sizeof (pname)); 1124*3431Scarlsonj if ((cp = strchr(pname, ':')) != NULL) 1125*3431Scarlsonj *cp = '\0'; 1126*3431Scarlsonj 1127*3431Scarlsonj if ((pif = lookup_pif_by_name(pname, isv6)) != NULL) 1128*3431Scarlsonj hold_pif(pif); 1129*3431Scarlsonj else if ((pif = insert_pif(pname, isv6, error)) == NULL) 1130*3431Scarlsonj return (NULL); 1131*3431Scarlsonj 1132*3431Scarlsonj if (lookup_lif_by_name(lname, pif) != NULL) { 1133*3431Scarlsonj dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!", 1134*3431Scarlsonj lname); 1135*3431Scarlsonj release_pif(pif); 1136*3431Scarlsonj *error = DHCP_IPC_E_INVIF; 1137*3431Scarlsonj return (NULL); 1138*3431Scarlsonj } 1139*3431Scarlsonj 1140*3431Scarlsonj /* If LIF creation fails, then insert_lif discards our PIF hold */ 1141*3431Scarlsonj return (insert_lif(pif, lname, error)); 1142*3431Scarlsonj } 1143*3431Scarlsonj 1144*3431Scarlsonj /* 1145*3431Scarlsonj * set_lif_dhcp(): Set logical interface flags to show that it's managed 1146*3431Scarlsonj * by DHCP. 1147*3431Scarlsonj * 1148*3431Scarlsonj * input: dhcp_lif_t *: the logical interface 1149*3431Scarlsonj * boolean_t: B_TRUE if adopting 1150*3431Scarlsonj * output: int: set to DHCP_IPC_E_* if operation fails 1151*3431Scarlsonj */ 1152*3431Scarlsonj 1153*3431Scarlsonj int 1154*3431Scarlsonj set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting) 1155*3431Scarlsonj { 1156*3431Scarlsonj int fd; 1157*3431Scarlsonj int err; 1158*3431Scarlsonj struct lifreq lifr; 1159*3431Scarlsonj 1160*3431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1161*3431Scarlsonj 1162*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1163*3431Scarlsonj 1164*3431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 1165*3431Scarlsonj err = errno; 1166*3431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s", 1167*3431Scarlsonj lif->lif_name); 1168*3431Scarlsonj return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT); 1169*3431Scarlsonj } 1170*3431Scarlsonj lif->lif_flags = lifr.lifr_flags; 1171*3431Scarlsonj 1172*3431Scarlsonj /* 1173*3431Scarlsonj * Check for conflicting sources of address control, and other 1174*3431Scarlsonj * unacceptable configurations. 1175*3431Scarlsonj */ 1176*3431Scarlsonj if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_MIPRUNNING| 1177*3431Scarlsonj IFF_TEMPORARY|IFF_VIRTUAL)) { 1178*3431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx", 1179*3431Scarlsonj lif->lif_name, lifr.lifr_flags); 1180*3431Scarlsonj return (DHCP_IPC_E_INVIF); 1181*3431Scarlsonj } 1182*3431Scarlsonj 11830Sstevel@tonic-gate /* 11840Sstevel@tonic-gate * if DHCPRUNNING is already set on the interface and we're 11850Sstevel@tonic-gate * not adopting it, the agent probably crashed and burned. 11860Sstevel@tonic-gate * note it, but don't let it stop the proceedings. we're 11870Sstevel@tonic-gate * pretty sure we're not already running, since we wouldn't 11880Sstevel@tonic-gate * have been able to bind to our IPC port. 11890Sstevel@tonic-gate */ 11900Sstevel@tonic-gate 1191*3431Scarlsonj if (lifr.lifr_flags & IFF_DHCPRUNNING) { 1192*3431Scarlsonj if (!is_adopting) { 1193*3431Scarlsonj dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already " 1194*3431Scarlsonj "set on %s", lif->lif_name); 1195*3431Scarlsonj } 1196*3431Scarlsonj } else { 1197*3431Scarlsonj lifr.lifr_flags |= IFF_DHCPRUNNING; 1198*3431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 1199*3431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s", 1200*3431Scarlsonj lif->lif_name); 1201*3431Scarlsonj return (DHCP_IPC_E_INT); 1202*3431Scarlsonj } 1203*3431Scarlsonj lif->lif_flags = lifr.lifr_flags; 12040Sstevel@tonic-gate } 1205*3431Scarlsonj return (DHCP_IPC_SUCCESS); 1206*3431Scarlsonj } 12070Sstevel@tonic-gate 1208*3431Scarlsonj /* 1209*3431Scarlsonj * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer 1210*3431Scarlsonj * managed by DHCP. 1211*3431Scarlsonj * 1212*3431Scarlsonj * input: dhcp_lif_t *: the logical interface 1213*3431Scarlsonj * output: none 1214*3431Scarlsonj */ 12150Sstevel@tonic-gate 1216*3431Scarlsonj static void 1217*3431Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif) 1218*3431Scarlsonj { 1219*3431Scarlsonj int fd; 1220*3431Scarlsonj struct lifreq lifr; 12210Sstevel@tonic-gate 1222*3431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1223*3431Scarlsonj 1224*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1225*3431Scarlsonj 1226*3431Scarlsonj if (!(lif->lif_flags & IFF_DHCPRUNNING)) 1227*3431Scarlsonj return; 12280Sstevel@tonic-gate 1229*3431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 1230*3431Scarlsonj return; 12310Sstevel@tonic-gate 1232*3431Scarlsonj if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) 1233*3431Scarlsonj return; 12340Sstevel@tonic-gate 1235*3431Scarlsonj lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING; 1236*3431Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 1237*3431Scarlsonj } 12380Sstevel@tonic-gate 1239*3431Scarlsonj /* 1240*3431Scarlsonj * set_lif_deprecated(): Set the "deprecated" flag to tell users that this 1241*3431Scarlsonj * address will be going away. As the interface is 1242*3431Scarlsonj * going away, we don't care if there are errors. 1243*3431Scarlsonj * 1244*3431Scarlsonj * input: dhcp_lif_t *: the logical interface 1245*3431Scarlsonj * output: none 1246*3431Scarlsonj */ 12470Sstevel@tonic-gate 1248*3431Scarlsonj void 1249*3431Scarlsonj set_lif_deprecated(dhcp_lif_t *lif) 1250*3431Scarlsonj { 1251*3431Scarlsonj int fd; 1252*3431Scarlsonj struct lifreq lifr; 12530Sstevel@tonic-gate 1254*3431Scarlsonj if (lif->lif_flags & IFF_DEPRECATED) 1255*3431Scarlsonj return; 12560Sstevel@tonic-gate 1257*3431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1258*3431Scarlsonj 1259*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 12600Sstevel@tonic-gate 1261*3431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 1262*3431Scarlsonj return; 1263*3431Scarlsonj 1264*3431Scarlsonj if (lifr.lifr_flags & IFF_DEPRECATED) 1265*3431Scarlsonj return; 12660Sstevel@tonic-gate 1267*3431Scarlsonj lifr.lifr_flags |= IFF_DEPRECATED; 1268*3431Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 1269*3431Scarlsonj lif->lif_flags = lifr.lifr_flags; 1270*3431Scarlsonj } 1271*3431Scarlsonj 1272*3431Scarlsonj /* 1273*3431Scarlsonj * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this 1274*3431Scarlsonj * address will not be going away. This happens if we 1275*3431Scarlsonj * get a renewal after preferred lifetime but before 1276*3431Scarlsonj * the valid lifetime. 1277*3431Scarlsonj * 1278*3431Scarlsonj * input: dhcp_lif_t *: the logical interface 1279*3431Scarlsonj * output: boolean_t: B_TRUE on success. 1280*3431Scarlsonj */ 12810Sstevel@tonic-gate 1282*3431Scarlsonj boolean_t 1283*3431Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif) 1284*3431Scarlsonj { 1285*3431Scarlsonj int fd; 1286*3431Scarlsonj struct lifreq lifr; 1287*3431Scarlsonj 1288*3431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1289*3431Scarlsonj 1290*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1291*3431Scarlsonj 1292*3431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 1293*3431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s", 1294*3431Scarlsonj lif->lif_name); 1295*3431Scarlsonj return (B_FALSE); 12960Sstevel@tonic-gate } 12970Sstevel@tonic-gate 12980Sstevel@tonic-gate /* 1299*3431Scarlsonj * Check for conflicting sources of address control, and other 1300*3431Scarlsonj * unacceptable configurations. 13010Sstevel@tonic-gate */ 1302*3431Scarlsonj if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_MIPRUNNING| 1303*3431Scarlsonj IFF_TEMPORARY|IFF_VIRTUAL)) { 1304*3431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags " 1305*3431Scarlsonj "are %llx", lif->lif_name, lifr.lifr_flags); 1306*3431Scarlsonj return (B_FALSE); 13070Sstevel@tonic-gate } 13080Sstevel@tonic-gate 1309*3431Scarlsonj if (!(lifr.lifr_flags & IFF_DEPRECATED)) 1310*3431Scarlsonj return (B_TRUE); 13110Sstevel@tonic-gate 1312*3431Scarlsonj lifr.lifr_flags &= ~IFF_DEPRECATED; 1313*3431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 1314*3431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s", 1315*3431Scarlsonj lif->lif_name); 1316*3431Scarlsonj return (B_FALSE); 1317*3431Scarlsonj } else { 1318*3431Scarlsonj lif->lif_flags = lifr.lifr_flags; 1319*3431Scarlsonj return (B_TRUE); 13200Sstevel@tonic-gate } 13210Sstevel@tonic-gate } 13220Sstevel@tonic-gate 13230Sstevel@tonic-gate /* 1324*3431Scarlsonj * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only). 13250Sstevel@tonic-gate * 1326*3431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 1327*3431Scarlsonj * output: boolean_t: B_TRUE if the socket was opened successfully. 13280Sstevel@tonic-gate */ 13290Sstevel@tonic-gate 1330*3431Scarlsonj boolean_t 1331*3431Scarlsonj open_ip_lif(dhcp_lif_t *lif) 13320Sstevel@tonic-gate { 1333*3431Scarlsonj if (lif->lif_sock_ip_fd != -1) { 1334*3431Scarlsonj dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s", 1335*3431Scarlsonj lif->lif_name); 1336*3431Scarlsonj return (B_FALSE); 13370Sstevel@tonic-gate } 13380Sstevel@tonic-gate 1339*3431Scarlsonj lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0); 1340*3431Scarlsonj if (lif->lif_sock_ip_fd == -1) { 1341*3431Scarlsonj dhcpmsg(MSG_ERR, "open_ip_lif: cannot create v4 socket on %s", 1342*3431Scarlsonj lif->lif_name); 1343*3431Scarlsonj return (B_FALSE); 13440Sstevel@tonic-gate } 13450Sstevel@tonic-gate 1346*3431Scarlsonj if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, 1347*3431Scarlsonj ntohl(lif->lif_addr))) { 1348*3431Scarlsonj dhcpmsg(MSG_ERR, "open_ip_lif: cannot bind v4 socket on %s", 1349*3431Scarlsonj lif->lif_name); 1350*3431Scarlsonj return (B_FALSE); 1351*3431Scarlsonj } 13520Sstevel@tonic-gate 1353*3431Scarlsonj lif->lif_acknak_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN, 1354*3431Scarlsonj dhcp_acknak_lif, lif); 1355*3431Scarlsonj if (lif->lif_acknak_id == -1) { 1356*3431Scarlsonj dhcpmsg(MSG_WARNING, "open_ip_lif: cannot register to " 1357*3431Scarlsonj "receive IP unicast"); 1358*3431Scarlsonj close_ip_lif(lif); 1359*3431Scarlsonj return (B_FALSE); 13600Sstevel@tonic-gate } 13610Sstevel@tonic-gate return (B_TRUE); 13620Sstevel@tonic-gate } 13630Sstevel@tonic-gate 13640Sstevel@tonic-gate /* 1365*3431Scarlsonj * close_ip_lif(): close an IP socket for I/O on a given LIF. 13660Sstevel@tonic-gate * 1367*3431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 1368*3431Scarlsonj * output: none 13690Sstevel@tonic-gate */ 13700Sstevel@tonic-gate 1371*3431Scarlsonj void 1372*3431Scarlsonj close_ip_lif(dhcp_lif_t *lif) 13730Sstevel@tonic-gate { 1374*3431Scarlsonj if (lif->lif_acknak_id != -1) { 1375*3431Scarlsonj (void) iu_unregister_event(eh, lif->lif_acknak_id, NULL); 1376*3431Scarlsonj lif->lif_acknak_id = -1; 1377*3431Scarlsonj } 1378*3431Scarlsonj if (lif->lif_sock_ip_fd != -1) { 1379*3431Scarlsonj (void) close(lif->lif_sock_ip_fd); 1380*3431Scarlsonj lif->lif_sock_ip_fd = -1; 1381*3431Scarlsonj } 1382*3431Scarlsonj } 13830Sstevel@tonic-gate 1384*3431Scarlsonj /* 1385*3431Scarlsonj * lif_mark_decline(): mark a LIF as having been declined due to a duplicate 1386*3431Scarlsonj * address or some other conflict. This is used in 1387*3431Scarlsonj * send_declines() to report failure back to the server. 1388*3431Scarlsonj * 1389*3431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 1390*3431Scarlsonj * const char *: text string explaining why the address is declined 1391*3431Scarlsonj * output: none 1392*3431Scarlsonj */ 13930Sstevel@tonic-gate 1394*3431Scarlsonj void 1395*3431Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason) 1396*3431Scarlsonj { 1397*3431Scarlsonj if (lif->lif_declined == NULL) { 1398*3431Scarlsonj dhcp_lease_t *dlp; 13990Sstevel@tonic-gate 1400*3431Scarlsonj lif->lif_declined = reason; 1401*3431Scarlsonj if ((dlp = lif->lif_lease) != NULL) 1402*3431Scarlsonj dlp->dl_smach->dsm_lif_down++; 14030Sstevel@tonic-gate } 14040Sstevel@tonic-gate } 14050Sstevel@tonic-gate 14060Sstevel@tonic-gate /* 1407*3431Scarlsonj * schedule_lif_timer(): schedules the LIF-related timer 14080Sstevel@tonic-gate * 1409*3431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 1410*3431Scarlsonj * dhcp_timer_t *: the timer to schedule 1411*3431Scarlsonj * iu_tq_callback_t *: the callback to call upon firing 1412*3431Scarlsonj * output: boolean_t: B_TRUE if the timer was scheduled successfully 14130Sstevel@tonic-gate */ 14140Sstevel@tonic-gate 1415*3431Scarlsonj boolean_t 1416*3431Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire) 14170Sstevel@tonic-gate { 14180Sstevel@tonic-gate /* 1419*3431Scarlsonj * If there's a timer running, cancel it and release its lease 1420*3431Scarlsonj * reference. 14210Sstevel@tonic-gate */ 1422*3431Scarlsonj if (dt->dt_id != -1) { 1423*3431Scarlsonj if (!cancel_timer(dt)) 1424*3431Scarlsonj return (B_FALSE); 1425*3431Scarlsonj release_lif(lif); 1426*3431Scarlsonj } 14270Sstevel@tonic-gate 1428*3431Scarlsonj if (schedule_timer(dt, expire, lif)) { 1429*3431Scarlsonj hold_lif(lif); 1430*3431Scarlsonj return (B_TRUE); 1431*3431Scarlsonj } else { 1432*3431Scarlsonj dhcpmsg(MSG_WARNING, 1433*3431Scarlsonj "schedule_lif_timer: cannot schedule timer"); 1434*3431Scarlsonj return (B_FALSE); 1435*3431Scarlsonj } 14360Sstevel@tonic-gate } 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate /* 1439*3431Scarlsonj * cancel_lif_timer(): cancels a LIF-related timer 14400Sstevel@tonic-gate * 1441*3431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 1442*3431Scarlsonj * dhcp_timer_t *: the timer to cancel 1443*3431Scarlsonj * output: none 14440Sstevel@tonic-gate */ 14450Sstevel@tonic-gate 1446*3431Scarlsonj static void 1447*3431Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt) 14480Sstevel@tonic-gate { 1449*3431Scarlsonj if (dt->dt_id == -1) 1450*3431Scarlsonj return; 1451*3431Scarlsonj if (cancel_timer(dt)) { 1452*3431Scarlsonj dhcpmsg(MSG_DEBUG2, 1453*3431Scarlsonj "cancel_lif_timer: canceled expiry timer on %s", 1454*3431Scarlsonj lif->lif_name); 1455*3431Scarlsonj release_lif(lif); 1456*3431Scarlsonj } else { 1457*3431Scarlsonj dhcpmsg(MSG_WARNING, 1458*3431Scarlsonj "cancel_lif_timer: cannot cancel timer on %s", 1459*3431Scarlsonj lif->lif_name); 14600Sstevel@tonic-gate } 14610Sstevel@tonic-gate } 14620Sstevel@tonic-gate 14630Sstevel@tonic-gate /* 1464*3431Scarlsonj * cancel_lif_timers(): cancels the LIF-related timers 14650Sstevel@tonic-gate * 1466*3431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 1467*3431Scarlsonj * output: none 14680Sstevel@tonic-gate */ 14690Sstevel@tonic-gate 14700Sstevel@tonic-gate void 1471*3431Scarlsonj cancel_lif_timers(dhcp_lif_t *lif) 14720Sstevel@tonic-gate { 1473*3431Scarlsonj cancel_lif_timer(lif, &lif->lif_preferred); 1474*3431Scarlsonj cancel_lif_timer(lif, &lif->lif_expire); 14750Sstevel@tonic-gate } 14760Sstevel@tonic-gate 14770Sstevel@tonic-gate /* 1478*3431Scarlsonj * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common 1479*3431Scarlsonj * file descriptors (v4_sock_fd and v6_sock_fd). 14800Sstevel@tonic-gate * 1481*3431Scarlsonj * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4 1482*3431Scarlsonj * output: none 14830Sstevel@tonic-gate */ 14840Sstevel@tonic-gate 1485*3431Scarlsonj uint_t 1486*3431Scarlsonj get_max_mtu(boolean_t isv6) 14870Sstevel@tonic-gate { 1488*3431Scarlsonj uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu; 1489*3431Scarlsonj 1490*3431Scarlsonj if (*mtup == 0) { 1491*3431Scarlsonj dhcp_pif_t *pif; 1492*3431Scarlsonj dhcp_lif_t *lif; 1493*3431Scarlsonj struct lifreq lifr; 1494*3431Scarlsonj 1495*3431Scarlsonj /* Set an arbitrary lower bound */ 1496*3431Scarlsonj *mtup = 1024; 1497*3431Scarlsonj pif = isv6 ? v6root : v4root; 1498*3431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 1499*3431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; 1500*3431Scarlsonj lif = lif->lif_next) { 1501*3431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, 1502*3431Scarlsonj LIFNAMSIZ); 1503*3431Scarlsonj if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) != 1504*3431Scarlsonj -1 && lifr.lifr_mtu > *mtup) { 1505*3431Scarlsonj *mtup = lifr.lifr_mtu; 1506*3431Scarlsonj } 1507*3431Scarlsonj } 1508*3431Scarlsonj } 15090Sstevel@tonic-gate } 1510*3431Scarlsonj return (*mtup); 15110Sstevel@tonic-gate } 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate /* 1514*3431Scarlsonj * expired_lif_state(): summarize the state of expired LIFs on a given state 1515*3431Scarlsonj * machine. 15160Sstevel@tonic-gate * 1517*3431Scarlsonj * input: dhcp_smach_t *: the state machine to scan 1518*3431Scarlsonj * output: dhcp_expire_t: overall state 15190Sstevel@tonic-gate */ 15200Sstevel@tonic-gate 1521*3431Scarlsonj dhcp_expire_t 1522*3431Scarlsonj expired_lif_state(dhcp_smach_t *dsmp) 15230Sstevel@tonic-gate { 1524*3431Scarlsonj dhcp_lease_t *dlp; 1525*3431Scarlsonj dhcp_lif_t *lif; 1526*3431Scarlsonj uint_t nlifs; 1527*3431Scarlsonj uint_t numlifs; 1528*3431Scarlsonj uint_t numexp; 15290Sstevel@tonic-gate 1530*3431Scarlsonj numlifs = numexp = 0; 1531*3431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1532*3431Scarlsonj lif = dlp->dl_lifs; 1533*3431Scarlsonj nlifs = dlp->dl_nlifs; 1534*3431Scarlsonj numlifs += nlifs; 1535*3431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 1536*3431Scarlsonj if (lif->lif_expired) 1537*3431Scarlsonj numexp++; 1538*3431Scarlsonj } 15390Sstevel@tonic-gate } 1540*3431Scarlsonj if (numlifs == 0) 1541*3431Scarlsonj return (DHCP_EXP_NOLIFS); 1542*3431Scarlsonj else if (numexp == 0) 1543*3431Scarlsonj return (DHCP_EXP_NOEXP); 1544*3431Scarlsonj else if (numlifs == numexp) 1545*3431Scarlsonj return (DHCP_EXP_ALLEXP); 1546*3431Scarlsonj else 1547*3431Scarlsonj return (DHCP_EXP_SOMEEXP); 15480Sstevel@tonic-gate } 15490Sstevel@tonic-gate 15500Sstevel@tonic-gate /* 1551*3431Scarlsonj * find_expired_lif(): find the first expired LIF on a given state machine 15520Sstevel@tonic-gate * 1553*3431Scarlsonj * input: dhcp_smach_t *: the state machine to scan 1554*3431Scarlsonj * output: dhcp_lif_t *: the first expired LIF, or NULL if none. 15550Sstevel@tonic-gate */ 15560Sstevel@tonic-gate 1557*3431Scarlsonj dhcp_lif_t * 1558*3431Scarlsonj find_expired_lif(dhcp_smach_t *dsmp) 15590Sstevel@tonic-gate { 1560*3431Scarlsonj dhcp_lease_t *dlp; 1561*3431Scarlsonj dhcp_lif_t *lif; 1562*3431Scarlsonj uint_t nlifs; 1563*3431Scarlsonj 1564*3431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1565*3431Scarlsonj lif = dlp->dl_lifs; 1566*3431Scarlsonj nlifs = dlp->dl_nlifs; 1567*3431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 1568*3431Scarlsonj if (lif->lif_expired) 1569*3431Scarlsonj return (lif); 1570*3431Scarlsonj } 1571*3431Scarlsonj } 1572*3431Scarlsonj return (NULL); 1573*3431Scarlsonj } 1574*3431Scarlsonj 1575*3431Scarlsonj /* 1576*3431Scarlsonj * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used 1577*3431Scarlsonj * only for DHCPv6. 1578*3431Scarlsonj * 1579*3431Scarlsonj * input: none 1580*3431Scarlsonj * output: none 1581*3431Scarlsonj */ 1582*3431Scarlsonj 1583*3431Scarlsonj void 1584*3431Scarlsonj remove_v6_strays(void) 1585*3431Scarlsonj { 1586*3431Scarlsonj struct lifnum lifn; 1587*3431Scarlsonj struct lifconf lifc; 1588*3431Scarlsonj struct lifreq *lifrp, *lifrmax; 1589*3431Scarlsonj uint_t numifs; 1590*3431Scarlsonj uint64_t flags; 15910Sstevel@tonic-gate 15920Sstevel@tonic-gate /* 1593*3431Scarlsonj * Get the approximate number of interfaces in the system. It's only 1594*3431Scarlsonj * approximate because the system is dynamic -- interfaces may be 1595*3431Scarlsonj * plumbed or unplumbed at any time. This is also the reason for the 1596*3431Scarlsonj * "+ 10" fudge factor: we're trying to avoid unnecessary looping. 15970Sstevel@tonic-gate */ 1598*3431Scarlsonj (void) memset(&lifn, 0, sizeof (lifn)); 1599*3431Scarlsonj lifn.lifn_family = AF_INET6; 1600*3431Scarlsonj lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 1601*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) { 1602*3431Scarlsonj dhcpmsg(MSG_ERR, 1603*3431Scarlsonj "remove_v6_strays: cannot read number of interfaces"); 1604*3431Scarlsonj numifs = 10; 1605*3431Scarlsonj } else { 1606*3431Scarlsonj numifs = lifn.lifn_count + 10; 16070Sstevel@tonic-gate } 16080Sstevel@tonic-gate 16090Sstevel@tonic-gate /* 1610*3431Scarlsonj * Get the interface information. We do this in a loop so that we can 1611*3431Scarlsonj * recover from EINVAL from the kernel -- delivered when the buffer is 1612*3431Scarlsonj * too small. 16130Sstevel@tonic-gate */ 1614*3431Scarlsonj (void) memset(&lifc, 0, sizeof (lifc)); 1615*3431Scarlsonj lifc.lifc_family = AF_INET6; 1616*3431Scarlsonj lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 1617*3431Scarlsonj for (;;) { 1618*3431Scarlsonj lifc.lifc_len = numifs * sizeof (*lifrp); 1619*3431Scarlsonj lifrp = realloc(lifc.lifc_buf, lifc.lifc_len); 1620*3431Scarlsonj if (lifrp == NULL) { 1621*3431Scarlsonj dhcpmsg(MSG_ERR, 1622*3431Scarlsonj "remove_v6_strays: cannot allocate memory"); 1623*3431Scarlsonj free(lifc.lifc_buf); 1624*3431Scarlsonj return; 16250Sstevel@tonic-gate } 1626*3431Scarlsonj lifc.lifc_buf = (caddr_t)lifrp; 1627*3431Scarlsonj errno = 0; 1628*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 && 1629*3431Scarlsonj lifc.lifc_len < numifs * sizeof (*lifrp)) 16300Sstevel@tonic-gate break; 1631*3431Scarlsonj if (errno == 0 || errno == EINVAL) { 1632*3431Scarlsonj numifs <<= 1; 1633*3431Scarlsonj } else { 1634*3431Scarlsonj dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF"); 1635*3431Scarlsonj free(lifc.lifc_buf); 1636*3431Scarlsonj return; 16370Sstevel@tonic-gate } 16380Sstevel@tonic-gate } 16390Sstevel@tonic-gate 1640*3431Scarlsonj lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp); 1641*3431Scarlsonj for (; lifrp < lifrmax; lifrp++) { 16420Sstevel@tonic-gate /* 1643*3431Scarlsonj * Get the interface flags; we're interested in the DHCP ones. 16440Sstevel@tonic-gate */ 1645*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1) 1646*3431Scarlsonj continue; 1647*3431Scarlsonj flags = lifrp->lifr_flags; 1648*3431Scarlsonj if (!(flags & IFF_DHCPRUNNING)) 1649*3431Scarlsonj continue; 16500Sstevel@tonic-gate /* 1651*3431Scarlsonj * If the interface has a link-local address, then we don't 1652*3431Scarlsonj * control it. Just remove the flag. 16530Sstevel@tonic-gate */ 1654*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1) 1655*3431Scarlsonj continue; 1656*3431Scarlsonj if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp-> 1657*3431Scarlsonj lifr_addr)->sin6_addr)) { 1658*3431Scarlsonj lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING; 1659*3431Scarlsonj (void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp); 1660*3431Scarlsonj continue; 16610Sstevel@tonic-gate } 16620Sstevel@tonic-gate /* 1663*3431Scarlsonj * All others are (or were) under our control. Clean up by 1664*3431Scarlsonj * removing them. 16650Sstevel@tonic-gate */ 1666*3431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) { 1667*3431Scarlsonj dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s", 1668*3431Scarlsonj lifrp->lifr_name); 1669*3431Scarlsonj } else if (errno != ENXIO) { 1670*3431Scarlsonj dhcpmsg(MSG_ERR, 1671*3431Scarlsonj "remove_v6_strays: SIOCLIFREMOVEIF %s", 1672*3431Scarlsonj lifrp->lifr_name); 16730Sstevel@tonic-gate } 16740Sstevel@tonic-gate } 1675*3431Scarlsonj free(lifc.lifc_buf); 16760Sstevel@tonic-gate } 1677