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