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 /* 225978Smeem * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #include <sys/types.h> 270Sstevel@tonic-gate #include <sys/socket.h> 280Sstevel@tonic-gate #include <net/if.h> 290Sstevel@tonic-gate #include <stdlib.h> 300Sstevel@tonic-gate #include <sys/sockio.h> 310Sstevel@tonic-gate #include <netinet/in.h> 320Sstevel@tonic-gate #include <netinet/dhcp.h> 330Sstevel@tonic-gate #include <string.h> 340Sstevel@tonic-gate #include <unistd.h> 353431Scarlsonj #include <search.h> 363431Scarlsonj #include <libdevinfo.h> 374456Sss150715 #include <libdlpi.h> 380Sstevel@tonic-gate #include <netinet/if_ether.h> 393431Scarlsonj #include <arpa/inet.h> 400Sstevel@tonic-gate #include <dhcpmsg.h> 413431Scarlsonj #include <dhcp_inittab.h> 420Sstevel@tonic-gate 433431Scarlsonj #include "agent.h" 440Sstevel@tonic-gate #include "interface.h" 450Sstevel@tonic-gate #include "util.h" 460Sstevel@tonic-gate #include "packet.h" 470Sstevel@tonic-gate #include "states.h" 483431Scarlsonj 493431Scarlsonj dhcp_pif_t *v4root; 503431Scarlsonj dhcp_pif_t *v6root; 513431Scarlsonj 523431Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu; 530Sstevel@tonic-gate 540Sstevel@tonic-gate /* 553431Scarlsonj * Interface flags to watch: things that should be under our direct control. 560Sstevel@tonic-gate */ 574823Sseb #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \ 584823Sseb IFF_TEMPORARY) 590Sstevel@tonic-gate 603431Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *); 610Sstevel@tonic-gate 620Sstevel@tonic-gate /* 633431Scarlsonj * insert_pif(): creates a new physical interface structure and chains it on 643431Scarlsonj * the list. Initializes state that remains consistent across 653431Scarlsonj * all use of the physical interface entry. 660Sstevel@tonic-gate * 673431Scarlsonj * input: const char *: the name of the physical interface 683431Scarlsonj * boolean_t: if B_TRUE, this is DHCPv6 693431Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 700Sstevel@tonic-gate * error code with the reason why 713431Scarlsonj * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure 720Sstevel@tonic-gate */ 730Sstevel@tonic-gate 743431Scarlsonj dhcp_pif_t * 753431Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error) 760Sstevel@tonic-gate { 773431Scarlsonj dhcp_pif_t *pif; 783431Scarlsonj struct lifreq lifr; 795381Smeem dlpi_handle_t dh = NULL; 805978Smeem int fd = isv6 ? v6_sock_fd : v4_sock_fd; 810Sstevel@tonic-gate 823431Scarlsonj if ((pif = calloc(1, sizeof (*pif))) == NULL) { 833431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for " 843431Scarlsonj "%s", pname); 853431Scarlsonj *error = DHCP_IPC_E_MEMORY; 860Sstevel@tonic-gate return (NULL); 870Sstevel@tonic-gate } 880Sstevel@tonic-gate 893431Scarlsonj pif->pif_isv6 = isv6; 903431Scarlsonj pif->pif_hold_count = 1; 913431Scarlsonj pif->pif_running = B_TRUE; 923431Scarlsonj 933431Scarlsonj if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) { 943431Scarlsonj dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long", 953431Scarlsonj pname); 963431Scarlsonj *error = DHCP_IPC_E_INVIF; 973431Scarlsonj goto failure; 983431Scarlsonj } 993431Scarlsonj 1005978Smeem /* 1015978Smeem * This is a bit gross, but IP has a confused interface. We must 1025978Smeem * assume that the zeroth LIF is plumbed, and must query there to get 1035978Smeem * the interface index number. 1045978Smeem */ 1055978Smeem (void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ); 1065978Smeem 1075978Smeem if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 1085978Smeem *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 1095978Smeem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname); 1105978Smeem goto failure; 1115978Smeem } 1125978Smeem pif->pif_index = lifr.lifr_index; 1133431Scarlsonj 1145978Smeem if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) { 1155978Smeem *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 1165978Smeem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname); 1175978Smeem goto failure; 1185978Smeem } 1195978Smeem pif->pif_max = lifr.lifr_mtu; 1203431Scarlsonj 1215978Smeem if (pif->pif_max < DHCP_DEF_MAX_SIZE) { 1225978Smeem dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to " 1235978Smeem "support DHCP (%u < %u)", pname, pif->pif_max, 1245978Smeem DHCP_DEF_MAX_SIZE); 1255978Smeem *error = DHCP_IPC_E_INVIF; 1265978Smeem goto failure; 1275978Smeem } 1285978Smeem 1295978Smeem /* 1305978Smeem * For IPv4, use DLPI to determine the hardware type, hardware 1315978Smeem * address, and hardware address length. 1325978Smeem */ 1335978Smeem if (!isv6) { 1345978Smeem int rc; 1355978Smeem dlpi_info_t dlinfo; 1365978Smeem 1374456Sss150715 if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) { 1384456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s", 1394456Sss150715 dlpi_strerror(rc)); 1404456Sss150715 *error = DHCP_IPC_E_INVIF; 1414456Sss150715 goto failure; 1424456Sss150715 } 1434456Sss150715 1444456Sss150715 if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) { 1454456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s", 1464456Sss150715 dlpi_strerror(rc)); 1473431Scarlsonj *error = DHCP_IPC_E_INVIF; 1483431Scarlsonj goto failure; 1493431Scarlsonj } 1503431Scarlsonj 1515978Smeem if ((rc = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) { 1524456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s", 1534456Sss150715 dlpi_strerror(rc)); 1544456Sss150715 *error = DHCP_IPC_E_INVIF; 1554456Sss150715 goto failure; 1564456Sss150715 } 1574456Sss150715 1584456Sss150715 pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype); 1594456Sss150715 pif->pif_hwlen = dlinfo.di_physaddrlen; 1603431Scarlsonj 1615381Smeem dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d", 1625381Smeem pname, pif->pif_hwtype, pif->pif_hwlen); 1633431Scarlsonj 1643431Scarlsonj if (pif->pif_hwlen > 0) { 1653431Scarlsonj pif->pif_hwaddr = malloc(pif->pif_hwlen); 1663431Scarlsonj if (pif->pif_hwaddr == NULL) { 1673431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate " 1683431Scarlsonj "pif_hwaddr for %s", pname); 1693431Scarlsonj *error = DHCP_IPC_E_MEMORY; 1703431Scarlsonj goto failure; 1713431Scarlsonj } 1725381Smeem (void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr, 1735381Smeem pif->pif_hwlen); 1743431Scarlsonj } 1753431Scarlsonj 1765381Smeem dlpi_close(dh); 1775381Smeem dh = NULL; 1783431Scarlsonj } 1793431Scarlsonj 1803431Scarlsonj insque(pif, isv6 ? &v6root : &v4root); 1813431Scarlsonj 1823431Scarlsonj return (pif); 1833431Scarlsonj failure: 1845381Smeem if (dh != NULL) 1855381Smeem dlpi_close(dh); 1863431Scarlsonj release_pif(pif); 1873431Scarlsonj return (NULL); 1883431Scarlsonj } 1893431Scarlsonj 1903431Scarlsonj /* 1913431Scarlsonj * hold_pif(): acquire a hold on a physical interface structure. 1923431Scarlsonj * 1933431Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure 1943431Scarlsonj * output: none 1953431Scarlsonj */ 1963431Scarlsonj 1973431Scarlsonj void 1983431Scarlsonj hold_pif(dhcp_pif_t *pif) 1993431Scarlsonj { 2003431Scarlsonj pif->pif_hold_count++; 2013431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name, 2023431Scarlsonj pif->pif_hold_count); 2033431Scarlsonj } 2043431Scarlsonj 2053431Scarlsonj /* 2063431Scarlsonj * release_pif(): release a hold on a physical interface structure; will 2073431Scarlsonj * destroy the structure on the last hold removed. 2083431Scarlsonj * 2093431Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure 2103431Scarlsonj * output: none 2113431Scarlsonj */ 2123431Scarlsonj 2133431Scarlsonj void 2143431Scarlsonj release_pif(dhcp_pif_t *pif) 2153431Scarlsonj { 2163431Scarlsonj if (pif->pif_hold_count == 0) { 2173431Scarlsonj dhcpmsg(MSG_CRIT, "release_pif: extraneous release"); 2183431Scarlsonj return; 2193431Scarlsonj } 2203431Scarlsonj 2213431Scarlsonj if (--pif->pif_hold_count == 0) { 2223431Scarlsonj dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s", 2233431Scarlsonj pif->pif_name); 2243431Scarlsonj 2253431Scarlsonj remque(pif); 2263431Scarlsonj free(pif->pif_hwaddr); 2273431Scarlsonj free(pif); 2283431Scarlsonj } else { 2293431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u", 2303431Scarlsonj pif->pif_name, pif->pif_hold_count); 2313431Scarlsonj } 2323431Scarlsonj } 2333431Scarlsonj 2343431Scarlsonj /* 2353431Scarlsonj * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and 2363431Scarlsonj * previous PIF pointer (or NULL for list start). 2373431Scarlsonj * Caller is expected to iterate through all 2383431Scarlsonj * potential matches to find interface of interest. 2393431Scarlsonj * 2403431Scarlsonj * input: uint16_t: the interface index (truncated) 2413431Scarlsonj * dhcp_pif_t *: the previous PIF, or NULL for list start 2423431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 2433431Scarlsonj * output: dhcp_pif_t *: the next matching PIF, or NULL if not found 2443431Scarlsonj * note: This operates using the 'truncated' (16-bit) ifindex as seen by 2453431Scarlsonj * routing socket clients. The value stored in pif_index is the 2463431Scarlsonj * 32-bit ifindex from the ioctl interface. 2473431Scarlsonj */ 2483431Scarlsonj 2493431Scarlsonj dhcp_pif_t * 2503431Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6) 2513431Scarlsonj { 2523431Scarlsonj if (pif == NULL) 2533431Scarlsonj pif = isv6 ? v6root : v4root; 2543431Scarlsonj else 2553431Scarlsonj pif = pif->pif_next; 2563431Scarlsonj 2573431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 2583431Scarlsonj if ((pif->pif_index & 0xffff) == ifindex) 2593431Scarlsonj break; 2603431Scarlsonj } 2613431Scarlsonj 2623431Scarlsonj return (pif); 2633431Scarlsonj } 2643431Scarlsonj 2653431Scarlsonj /* 2663431Scarlsonj * lookup_pif_by_name(): Looks up a physical interface entry given a name. 2673431Scarlsonj * 2683431Scarlsonj * input: const char *: the physical interface name 2693431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 2703431Scarlsonj * output: dhcp_pif_t *: the matching PIF, or NULL if not found 2713431Scarlsonj */ 2723431Scarlsonj 2733431Scarlsonj dhcp_pif_t * 2743431Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6) 2753431Scarlsonj { 2763431Scarlsonj dhcp_pif_t *pif; 2773431Scarlsonj 2783431Scarlsonj pif = isv6 ? v6root : v4root; 2793431Scarlsonj 2803431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 2813431Scarlsonj if (strcmp(pif->pif_name, pname) == 0) 2823431Scarlsonj break; 2833431Scarlsonj } 2843431Scarlsonj 2853431Scarlsonj return (pif); 2863431Scarlsonj } 2873431Scarlsonj 2883431Scarlsonj /* 2893431Scarlsonj * pif_status(): update the physical interface up/down status. 2903431Scarlsonj * 2915381Smeem * input: dhcp_pif_t *: the physical interface to be updated 2923431Scarlsonj * boolean_t: B_TRUE if the interface is going up 2933431Scarlsonj * output: none 2943431Scarlsonj */ 2953431Scarlsonj 2963431Scarlsonj void 2973431Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup) 2983431Scarlsonj { 2993431Scarlsonj dhcp_lif_t *lif; 3003431Scarlsonj dhcp_smach_t *dsmp; 3013431Scarlsonj 3023431Scarlsonj pif->pif_running = isup; 3034516Scarlsonj dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name, 3043431Scarlsonj isup ? "come back up" : "gone down"); 3053431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 3063431Scarlsonj for (dsmp = lif->lif_smachs; dsmp != NULL; 3073431Scarlsonj dsmp = dsmp->dsm_next) { 3083431Scarlsonj if (isup) 3093431Scarlsonj refresh_smach(dsmp); 3103431Scarlsonj else 3113431Scarlsonj remove_default_routes(dsmp); 3123431Scarlsonj } 3133431Scarlsonj } 3143431Scarlsonj } 3153431Scarlsonj 3163431Scarlsonj /* Helper for insert_lif: extract addresses as defined */ 3173431Scarlsonj #define ASSIGN_ADDR(v4, v6, lf) \ 3183431Scarlsonj if (pif->pif_isv6) { \ 3193431Scarlsonj lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \ 3203431Scarlsonj } else { \ 3213431Scarlsonj lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \ 3223431Scarlsonj } 3233431Scarlsonj 3243431Scarlsonj /* 3253431Scarlsonj * insert_lif(): Creates a new logical interface structure and chains it on 3263431Scarlsonj * the list for a given physical interface. Initializes state 3273431Scarlsonj * that remains consistent across all use of the logical 3283431Scarlsonj * interface entry. Caller's PIF hold is transferred to the 3293431Scarlsonj * LIF on success, and is dropped on failure. 3303431Scarlsonj * 3313431Scarlsonj * input: dhcp_pif_t *: pointer to the physical interface for this LIF 3323431Scarlsonj * const char *: the name of the logical interface 3333431Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 3343431Scarlsonj * error code with the reason why 3353431Scarlsonj * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure 3363431Scarlsonj */ 3373431Scarlsonj 3383431Scarlsonj dhcp_lif_t * 3393431Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error) 3403431Scarlsonj { 3413431Scarlsonj dhcp_lif_t *lif; 3423431Scarlsonj int fd; 3433431Scarlsonj struct lifreq lifr; 3443431Scarlsonj 3453431Scarlsonj if ((lif = calloc(1, sizeof (*lif))) == NULL) { 3463431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for " 3473431Scarlsonj "%s", lname); 3480Sstevel@tonic-gate *error = DHCP_IPC_E_MEMORY; 3490Sstevel@tonic-gate return (NULL); 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate 3523431Scarlsonj lif->lif_sock_ip_fd = -1; 3535381Smeem lif->lif_packet_id = -1; 3543431Scarlsonj lif->lif_iaid_id = -1; 3553431Scarlsonj lif->lif_hold_count = 1; 3563431Scarlsonj lif->lif_pif = pif; 3573431Scarlsonj lif->lif_removed = B_TRUE; 3583431Scarlsonj init_timer(&lif->lif_preferred, 0); 3593431Scarlsonj init_timer(&lif->lif_expire, 0); 3600Sstevel@tonic-gate 3613431Scarlsonj if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) { 3623431Scarlsonj dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long", 3633431Scarlsonj lname); 3640Sstevel@tonic-gate *error = DHCP_IPC_E_INVIF; 3650Sstevel@tonic-gate goto failure; 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate 3683431Scarlsonj (void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ); 3693431Scarlsonj 3703431Scarlsonj fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 3710Sstevel@tonic-gate 3723431Scarlsonj if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) 3733431Scarlsonj lif->lif_max = 1024; 3743431Scarlsonj else 3753431Scarlsonj lif->lif_max = lifr.lifr_mtu; 3760Sstevel@tonic-gate 3773431Scarlsonj if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) { 3783431Scarlsonj if (errno == ENXIO) 3793431Scarlsonj *error = DHCP_IPC_E_INVIF; 3803431Scarlsonj else 3813431Scarlsonj *error = DHCP_IPC_E_INT; 3823431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname); 3830Sstevel@tonic-gate goto failure; 3840Sstevel@tonic-gate } 3853431Scarlsonj ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr); 3860Sstevel@tonic-gate 3873431Scarlsonj if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 3882546Scarlsonj if (errno == ENXIO) 3892546Scarlsonj *error = DHCP_IPC_E_INVIF; 3902546Scarlsonj else 3912546Scarlsonj *error = DHCP_IPC_E_INT; 3923431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname); 3933431Scarlsonj goto failure; 3943431Scarlsonj } 3953431Scarlsonj ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr); 3963431Scarlsonj 3973431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 3983431Scarlsonj *error = DHCP_IPC_E_INT; 3993431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname); 4002546Scarlsonj goto failure; 4012546Scarlsonj } 4023431Scarlsonj lif->lif_flags = lifr.lifr_flags; 4033431Scarlsonj 4043431Scarlsonj /* 4053431Scarlsonj * If we've just detected the interface going up or down, then signal 4063431Scarlsonj * an appropriate action. There may be other state machines here. 4073431Scarlsonj */ 4083431Scarlsonj if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) { 4093431Scarlsonj pif_status(pif, B_TRUE); 4103431Scarlsonj } else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) { 4113431Scarlsonj pif_status(pif, B_FALSE); 4123431Scarlsonj } 4133431Scarlsonj 4143431Scarlsonj if (lifr.lifr_flags & IFF_POINTOPOINT) { 4153431Scarlsonj if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) { 4163431Scarlsonj *error = DHCP_IPC_E_INT; 4173431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s", 4183431Scarlsonj lname); 4193431Scarlsonj goto failure; 4203431Scarlsonj } 4213431Scarlsonj ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr); 4223431Scarlsonj } else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) { 4233431Scarlsonj if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) { 4243431Scarlsonj *error = DHCP_IPC_E_INT; 4253431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s", 4263431Scarlsonj lname); 4273431Scarlsonj goto failure; 4283431Scarlsonj } 4293431Scarlsonj lif->lif_broadcast = 4303431Scarlsonj ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr. 4313431Scarlsonj s_addr; 4323431Scarlsonj } 4333431Scarlsonj 4343431Scarlsonj if (pif->pif_isv6) 4353431Scarlsonj cached_v6_max_mtu = 0; 4363431Scarlsonj else 4373431Scarlsonj cached_v4_max_mtu = 0; 4383431Scarlsonj 4393431Scarlsonj lif->lif_removed = B_FALSE; 4403431Scarlsonj insque(lif, &pif->pif_lifs); 4413431Scarlsonj 4423431Scarlsonj return (lif); 4433431Scarlsonj 4443431Scarlsonj failure: 4453431Scarlsonj release_lif(lif); 4463431Scarlsonj return (NULL); 4473431Scarlsonj } 4483431Scarlsonj 4493431Scarlsonj /* 4503431Scarlsonj * hold_lif(): acquire a hold on a logical interface structure. 4513431Scarlsonj * 4523431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 4533431Scarlsonj * output: none 4543431Scarlsonj */ 4553431Scarlsonj 4563431Scarlsonj void 4573431Scarlsonj hold_lif(dhcp_lif_t *lif) 4583431Scarlsonj { 4593431Scarlsonj lif->lif_hold_count++; 4603431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name, 4613431Scarlsonj lif->lif_hold_count); 4623431Scarlsonj } 4633431Scarlsonj 4643431Scarlsonj /* 4653431Scarlsonj * release_lif(): release a hold on a logical interface structure; will 4663431Scarlsonj * destroy the structure on the last hold removed. 4673431Scarlsonj * 4683431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 4693431Scarlsonj * output: none 4703431Scarlsonj */ 4713431Scarlsonj 4723431Scarlsonj void 4733431Scarlsonj release_lif(dhcp_lif_t *lif) 4743431Scarlsonj { 4753431Scarlsonj if (lif->lif_hold_count == 0) { 4763431Scarlsonj dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s", 4773431Scarlsonj lif->lif_name); 4783431Scarlsonj return; 4793431Scarlsonj } 4803431Scarlsonj 4813431Scarlsonj if (lif->lif_hold_count == 1 && !lif->lif_removed) { 4823431Scarlsonj unplumb_lif(lif); 4833431Scarlsonj return; 4843431Scarlsonj } 4853431Scarlsonj 4863431Scarlsonj if (--lif->lif_hold_count == 0) { 4873431Scarlsonj dhcp_pif_t *pif; 4883431Scarlsonj 4893431Scarlsonj dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s", 4903431Scarlsonj lif->lif_name); 4913431Scarlsonj 4923431Scarlsonj if (lif->lif_lease != NULL) 4933431Scarlsonj dhcpmsg(MSG_CRIT, 4943431Scarlsonj "release_lif: still holding lease at last hold!"); 4953431Scarlsonj close_ip_lif(lif); 4963431Scarlsonj pif = lif->lif_pif; 4973431Scarlsonj if (pif->pif_isv6) 4983431Scarlsonj cached_v6_max_mtu = 0; 4993431Scarlsonj else 5003431Scarlsonj cached_v4_max_mtu = 0; 5013431Scarlsonj release_pif(pif); 5023431Scarlsonj free(lif); 5033431Scarlsonj } else { 5043431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u", 5053431Scarlsonj lif->lif_name, lif->lif_hold_count); 5063431Scarlsonj } 5073431Scarlsonj } 5083431Scarlsonj 5093431Scarlsonj /* 5103431Scarlsonj * remove_lif(): remove a logical interface from its PIF and lease (if any) and 5113431Scarlsonj * the lease's hold on the LIF. Assumes that we did not plumb 5123431Scarlsonj * the interface. 5133431Scarlsonj * 5143431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 5153431Scarlsonj * output: none 5163431Scarlsonj */ 5173431Scarlsonj 5183431Scarlsonj void 5193431Scarlsonj remove_lif(dhcp_lif_t *lif) 5203431Scarlsonj { 5213431Scarlsonj if (lif->lif_plumbed) { 5223431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s", 5233431Scarlsonj lif->lif_name); 5243431Scarlsonj return; 5253431Scarlsonj } 5263431Scarlsonj if (lif->lif_removed) { 5273431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s", 5283431Scarlsonj lif->lif_name); 5293431Scarlsonj } else { 5303431Scarlsonj dhcp_lif_t *lifnext; 5313431Scarlsonj dhcp_lease_t *dlp; 5323431Scarlsonj 5333431Scarlsonj dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name); 5343431Scarlsonj lif->lif_removed = B_TRUE; 5353431Scarlsonj lifnext = lif->lif_next; 5363431Scarlsonj clear_lif_dhcp(lif); 5373431Scarlsonj cancel_lif_timers(lif); 5383431Scarlsonj if (lif->lif_iaid_id != -1 && 5393431Scarlsonj iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) { 5403431Scarlsonj lif->lif_iaid_id = -1; 5413431Scarlsonj release_lif(lif); 5423431Scarlsonj } 5433431Scarlsonj 5443431Scarlsonj /* Remove from PIF list */ 5453431Scarlsonj remque(lif); 5463431Scarlsonj 5473431Scarlsonj /* If we were part of a lease, then remove ourselves */ 5483431Scarlsonj if ((dlp = lif->lif_lease) != NULL) { 5493431Scarlsonj if (--dlp->dl_nlifs == 0) 5503431Scarlsonj dlp->dl_lifs = NULL; 5513431Scarlsonj else if (dlp->dl_lifs == lif) 5523431Scarlsonj dlp->dl_lifs = lifnext; 5533431Scarlsonj if (lif->lif_declined != NULL) { 5543431Scarlsonj dlp->dl_smach->dsm_lif_down--; 5553431Scarlsonj lif->lif_declined = NULL; 5563431Scarlsonj } 5573431Scarlsonj lif->lif_lease = NULL; 5583431Scarlsonj release_lif(lif); 5593431Scarlsonj } 5603431Scarlsonj } 5613431Scarlsonj } 5623431Scarlsonj 5633431Scarlsonj /* 5643431Scarlsonj * lookup_lif_by_name(): Looks up a logical interface entry given a name and 5653431Scarlsonj * a physical interface. 5663431Scarlsonj * 5673431Scarlsonj * input: const char *: the logical interface name 5683431Scarlsonj * const dhcp_pif_t *: the physical interface 5693431Scarlsonj * output: dhcp_lif_t *: the matching LIF, or NULL if not found 5703431Scarlsonj */ 5713431Scarlsonj 5723431Scarlsonj dhcp_lif_t * 5733431Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif) 5743431Scarlsonj { 5753431Scarlsonj dhcp_lif_t *lif; 5763431Scarlsonj 5773431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 5783431Scarlsonj if (strcmp(lif->lif_name, lname) == 0) 5793431Scarlsonj break; 5803431Scarlsonj } 5813431Scarlsonj 5823431Scarlsonj return (lif); 5833431Scarlsonj } 5843431Scarlsonj 5853431Scarlsonj /* 5863431Scarlsonj * checkaddr(): checks if the given address is still set on the given LIF 5873431Scarlsonj * 5883431Scarlsonj * input: const dhcp_lif_t *: the LIF to check 5893431Scarlsonj * int: the address to look up on the interface (ioctl) 5903431Scarlsonj * const in6_addr_t *: the address to compare to 5913431Scarlsonj * const char *: name of the address for logging purposes 5923431Scarlsonj * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not 5933431Scarlsonj */ 5943431Scarlsonj 5953431Scarlsonj static boolean_t 5963431Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr, 5973431Scarlsonj const char *aname) 5983431Scarlsonj { 5993431Scarlsonj boolean_t isv6; 6003431Scarlsonj int fd; 6013431Scarlsonj struct lifreq lifr; 6025381Smeem char abuf1[INET6_ADDRSTRLEN]; 6035381Smeem char abuf2[INET6_ADDRSTRLEN]; 6043431Scarlsonj 6053431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 6063431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 6073431Scarlsonj 6083431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 6093431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 6103431Scarlsonj 6113431Scarlsonj if (ioctl(fd, ioccmd, &lifr) == -1) { 6123431Scarlsonj if (errno == ENXIO) { 6133431Scarlsonj dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone", 6143431Scarlsonj lif->lif_name); 6153431Scarlsonj return (B_FALSE); 6163431Scarlsonj } 6173431Scarlsonj dhcpmsg(MSG_DEBUG, 6183431Scarlsonj "checkaddr: ignoring ioctl error on %s %x: %s", 6193431Scarlsonj lif->lif_name, ioccmd, strerror(errno)); 6203431Scarlsonj } else if (isv6) { 6213431Scarlsonj struct sockaddr_in6 *sin6 = 6223431Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr; 6233431Scarlsonj 6243431Scarlsonj if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) { 6253431Scarlsonj dhcpmsg(MSG_WARNING, 6265381Smeem "checkaddr: expected %s %s on %s, have %s", aname, 6275381Smeem inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)), 6285381Smeem lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr, 6295381Smeem abuf2, sizeof (abuf2))); 6303431Scarlsonj return (B_FALSE); 6313431Scarlsonj } 6323431Scarlsonj } else { 6333431Scarlsonj struct sockaddr_in *sinp = 6343431Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr; 6353431Scarlsonj ipaddr_t v4addr; 6363431Scarlsonj 6373431Scarlsonj IN6_V4MAPPED_TO_IPADDR(addr, v4addr); 6383431Scarlsonj if (sinp->sin_addr.s_addr != v4addr) { 6393431Scarlsonj dhcpmsg(MSG_WARNING, 6405381Smeem "checkaddr: expected %s %s on %s, have %s", aname, 6415381Smeem inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)), 6425381Smeem lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr, 6435381Smeem abuf2, sizeof (abuf2))); 6443431Scarlsonj return (B_FALSE); 6453431Scarlsonj } 6463431Scarlsonj } 6473431Scarlsonj return (B_TRUE); 6483431Scarlsonj } 6493431Scarlsonj 6503431Scarlsonj /* 6513431Scarlsonj * verify_lif(): verifies than a LIF is still valid (i.e., has not been 6523431Scarlsonj * explicitly or implicitly dropped or released) 6533431Scarlsonj * 6543431Scarlsonj * input: const dhcp_lif_t *: the LIF to verify 6553431Scarlsonj * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise 6563431Scarlsonj */ 6573431Scarlsonj 6583431Scarlsonj boolean_t 6593431Scarlsonj verify_lif(const dhcp_lif_t *lif) 6603431Scarlsonj { 6613431Scarlsonj boolean_t isv6; 6623431Scarlsonj int fd; 6633431Scarlsonj struct lifreq lifr; 6643431Scarlsonj 6653431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 6663431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 6673431Scarlsonj 6683431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 6693431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 6703431Scarlsonj 6713431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 6724106Scarlsonj if (errno != ENXIO) { 6734106Scarlsonj dhcpmsg(MSG_ERR, 6744106Scarlsonj "verify_lif: SIOCGLIFFLAGS failed on %s", 6754106Scarlsonj lif->lif_name); 6764106Scarlsonj } 6773431Scarlsonj return (B_FALSE); 6783431Scarlsonj } 6793431Scarlsonj 6803431Scarlsonj /* 6813431Scarlsonj * If important flags have changed, then abandon the interface. 6823431Scarlsonj */ 6833431Scarlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) { 6843431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: " 6853431Scarlsonj "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags, 6863431Scarlsonj lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) & 6873431Scarlsonj DHCP_IFF_WATCH); 6883431Scarlsonj return (B_FALSE); 6893431Scarlsonj } 6903431Scarlsonj 6913431Scarlsonj /* 6923431Scarlsonj * Special case: if the interface has gone down as a duplicate, then 6933431Scarlsonj * this alone does _not_ mean that we're abandoning it just yet. Allow 6943431Scarlsonj * the state machine to handle this normally by trying to get a new 6953431Scarlsonj * lease. 6963431Scarlsonj */ 6973431Scarlsonj if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) { 6983431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s", 6993431Scarlsonj lif->lif_name); 7003431Scarlsonj return (B_TRUE); 7013431Scarlsonj } 7023431Scarlsonj 7033431Scarlsonj /* 7043431Scarlsonj * If the user has torn down or started up the interface manually, then 7053431Scarlsonj * abandon the lease. 7063431Scarlsonj */ 7073431Scarlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) { 7083431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s", 7093431Scarlsonj lifr.lifr_flags & IFF_UP ? "started up" : "shut down", 7103431Scarlsonj lif->lif_name); 7113431Scarlsonj return (B_FALSE); 7123431Scarlsonj } 7133431Scarlsonj 7143431Scarlsonj /* 7153431Scarlsonj * Check for delete and recreate. 7163431Scarlsonj */ 7173431Scarlsonj if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 7183431Scarlsonj dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s", 7193431Scarlsonj lif->lif_name); 7203431Scarlsonj return (B_FALSE); 7213431Scarlsonj } 7223431Scarlsonj if (lifr.lifr_index != lif->lif_pif->pif_index) { 7233431Scarlsonj dhcpmsg(MSG_DEBUG, 7243431Scarlsonj "verify_lif: ifindex on %s changed: %u to %u", 7253431Scarlsonj lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index); 7263431Scarlsonj return (B_FALSE); 7273431Scarlsonj } 7283431Scarlsonj 7293431Scarlsonj /* 7303431Scarlsonj * If the IP address, netmask, or broadcast address have changed, or 7313431Scarlsonj * the interface has been unplumbed, then we act like there has been an 7323431Scarlsonj * implicit drop. (Note that the netmask is under DHCP control for 7333431Scarlsonj * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast 7343431Scarlsonj * addresses.) 7353431Scarlsonj */ 7362546Scarlsonj 7373431Scarlsonj if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address")) 7383431Scarlsonj return (B_FALSE); 7393431Scarlsonj 7403431Scarlsonj if (isv6) { 7413431Scarlsonj /* 7423431Scarlsonj * If it's not point-to-point, we're done. If it is, then 7433431Scarlsonj * check the peer's address as well. 7443431Scarlsonj */ 7453431Scarlsonj return (!(lif->lif_flags & IFF_POINTOPOINT) || 7463431Scarlsonj checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer, 7473431Scarlsonj "peer address")); 7483431Scarlsonj } else { 7493431Scarlsonj if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask, 7503431Scarlsonj "netmask")) 7513431Scarlsonj return (B_FALSE); 7523431Scarlsonj 7533431Scarlsonj return (checkaddr(lif, 7543431Scarlsonj (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR : 7553431Scarlsonj SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address")); 7563431Scarlsonj } 7573431Scarlsonj } 7583431Scarlsonj 7593431Scarlsonj /* 7603431Scarlsonj * canonize_lif(): puts the interface in a canonical (zeroed) form. This is 7613431Scarlsonj * used only on the "main" LIF for IPv4. All other interfaces 7623431Scarlsonj * are under dhcpagent control and are removed using 7633431Scarlsonj * unplumb_lif(). 7643431Scarlsonj * 7653431Scarlsonj * input: dhcp_lif_t *: the interface to canonize 7665381Smeem * boolean_t: only canonize lif if it's under DHCP control 7673431Scarlsonj * output: none 7683431Scarlsonj */ 7693431Scarlsonj 7703431Scarlsonj static void 7715381Smeem canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly) 7723431Scarlsonj { 7733431Scarlsonj boolean_t isv6; 7743431Scarlsonj int fd; 7753431Scarlsonj struct lifreq lifr; 7763431Scarlsonj 7773431Scarlsonj /* 7783431Scarlsonj * If there's nothing here, then don't touch the interface. This can 7793431Scarlsonj * happen when an already-canonized LIF is recanonized. 7803431Scarlsonj */ 7813431Scarlsonj if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr)) 7823431Scarlsonj return; 7833431Scarlsonj 7843431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 7853431Scarlsonj dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s", 7863431Scarlsonj isv6 ? 6 : 4, lif->lif_name); 7873431Scarlsonj 7883431Scarlsonj lif->lif_v6addr = my_in6addr_any; 7893431Scarlsonj lif->lif_v6mask = my_in6addr_any; 7903431Scarlsonj lif->lif_v6peer = my_in6addr_any; 7913431Scarlsonj 7923431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 7933431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 7943431Scarlsonj 7953431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 7963431Scarlsonj 7973431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 7984106Scarlsonj if (errno != ENXIO) { 7994106Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s", 8004106Scarlsonj lif->lif_name); 8014106Scarlsonj } 8023431Scarlsonj return; 8033431Scarlsonj } 8046637Smeem lif->lif_flags = lifr.lifr_flags; 8053431Scarlsonj 8065381Smeem if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) { 8073431Scarlsonj dhcpmsg(MSG_INFO, 8083431Scarlsonj "canonize_lif: cannot clear %s; flags are %llx", 8093431Scarlsonj lif->lif_name, lifr.lifr_flags); 8103431Scarlsonj return; 8113431Scarlsonj } 8123431Scarlsonj 8133431Scarlsonj (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); 8143431Scarlsonj if (isv6) { 8153431Scarlsonj struct sockaddr_in6 *sin6 = 8163431Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr; 8173431Scarlsonj 8183431Scarlsonj sin6->sin6_family = AF_INET6; 8193431Scarlsonj sin6->sin6_addr = my_in6addr_any; 8203431Scarlsonj } else { 8213431Scarlsonj struct sockaddr_in *sinv = 8223431Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr; 8233431Scarlsonj 8243431Scarlsonj sinv->sin_family = AF_INET; 8253431Scarlsonj sinv->sin_addr.s_addr = htonl(INADDR_ANY); 8263431Scarlsonj } 8273431Scarlsonj 8283431Scarlsonj if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) { 8293431Scarlsonj dhcpmsg(MSG_ERR, 8303431Scarlsonj "canonize_lif: can't clear local address on %s", 8313431Scarlsonj lif->lif_name); 8323431Scarlsonj } 8333431Scarlsonj 8343431Scarlsonj if (lif->lif_flags & IFF_POINTOPOINT) { 8353431Scarlsonj if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) { 8363431Scarlsonj dhcpmsg(MSG_ERR, 8373431Scarlsonj "canonize_lif: can't clear remote address on %s", 8383431Scarlsonj lif->lif_name); 8393431Scarlsonj } 8403431Scarlsonj } else if (!isv6) { 8413431Scarlsonj if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) { 8423431Scarlsonj dhcpmsg(MSG_ERR, 8433431Scarlsonj "canonize_lif: can't clear broadcast address on %s", 8443431Scarlsonj lif->lif_name); 8453431Scarlsonj } 8463431Scarlsonj } 847*8403SAnurag.Maskey@Sun.COM 848*8403SAnurag.Maskey@Sun.COM /* 849*8403SAnurag.Maskey@Sun.COM * Clear the netmask last as it has to be refetched after clearing. 850*8403SAnurag.Maskey@Sun.COM * Netmask is under in.ndpd control with IPv6. 851*8403SAnurag.Maskey@Sun.COM */ 852*8403SAnurag.Maskey@Sun.COM if (!isv6) { 853*8403SAnurag.Maskey@Sun.COM /* Clear the netmask */ 854*8403SAnurag.Maskey@Sun.COM if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) { 855*8403SAnurag.Maskey@Sun.COM dhcpmsg(MSG_ERR, 856*8403SAnurag.Maskey@Sun.COM "canonize_lif: can't clear netmask on %s", 857*8403SAnurag.Maskey@Sun.COM lif->lif_name); 858*8403SAnurag.Maskey@Sun.COM } else { 859*8403SAnurag.Maskey@Sun.COM /* 860*8403SAnurag.Maskey@Sun.COM * When the netmask is cleared, the kernel actually sets 861*8403SAnurag.Maskey@Sun.COM * the netmask to 255.0.0.0. So, refetch that netmask. 862*8403SAnurag.Maskey@Sun.COM */ 863*8403SAnurag.Maskey@Sun.COM if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 864*8403SAnurag.Maskey@Sun.COM dhcpmsg(MSG_ERR, 865*8403SAnurag.Maskey@Sun.COM "canonize_lif: can't reload cleared " 866*8403SAnurag.Maskey@Sun.COM "netmask on %s", lif->lif_name); 867*8403SAnurag.Maskey@Sun.COM } else { 868*8403SAnurag.Maskey@Sun.COM /* Refetch succeeded, update LIF */ 869*8403SAnurag.Maskey@Sun.COM lif->lif_netmask = 870*8403SAnurag.Maskey@Sun.COM ((struct sockaddr_in *)&lifr.lifr_addr)-> 871*8403SAnurag.Maskey@Sun.COM sin_addr.s_addr; 872*8403SAnurag.Maskey@Sun.COM } 873*8403SAnurag.Maskey@Sun.COM } 874*8403SAnurag.Maskey@Sun.COM } 8753431Scarlsonj } 8763431Scarlsonj 8773431Scarlsonj /* 8783431Scarlsonj * plumb_lif(): Adds the LIF to the system. This is used for all 8793431Scarlsonj * DHCPv6-derived interfaces. The returned LIF has a hold 8803431Scarlsonj * on it. 8813431Scarlsonj * 8823431Scarlsonj * input: dhcp_lif_t *: the interface to unplumb 8833431Scarlsonj * output: none 8843431Scarlsonj */ 8853431Scarlsonj 8863431Scarlsonj dhcp_lif_t * 8873431Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr) 8883431Scarlsonj { 8893431Scarlsonj dhcp_lif_t *lif; 8903431Scarlsonj char abuf[INET6_ADDRSTRLEN]; 8913431Scarlsonj struct lifreq lifr; 8923431Scarlsonj struct sockaddr_in6 *sin6; 8933431Scarlsonj int error; 8943431Scarlsonj 8953431Scarlsonj (void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)); 8963431Scarlsonj 8973431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 8983431Scarlsonj if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) { 8993431Scarlsonj dhcpmsg(MSG_ERR, 9003431Scarlsonj "plumb_lif: entry for %s already exists!", abuf); 9013431Scarlsonj return (NULL); 9023431Scarlsonj } 9033431Scarlsonj } 9043431Scarlsonj 9053431Scarlsonj /* First, create a new zero-address logical interface */ 9063431Scarlsonj (void) memset(&lifr, 0, sizeof (lifr)); 9073431Scarlsonj (void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name)); 9083431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) { 9093431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name); 9103431Scarlsonj return (NULL); 9113431Scarlsonj } 9123431Scarlsonj 9133431Scarlsonj /* Next, set the netmask to all ones */ 9143431Scarlsonj sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 9153431Scarlsonj sin6->sin6_family = AF_INET6; 9163431Scarlsonj (void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr)); 9173431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 9183431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s", 9193431Scarlsonj lifr.lifr_name); 9200Sstevel@tonic-gate goto failure; 9210Sstevel@tonic-gate } 9220Sstevel@tonic-gate 9233431Scarlsonj /* Now set the interface address */ 9243431Scarlsonj sin6->sin6_addr = *addr; 9253431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 9263431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s", 9273431Scarlsonj lifr.lifr_name, abuf); 9283431Scarlsonj goto failure; 9293431Scarlsonj } 9303431Scarlsonj 9313431Scarlsonj /* Mark the interface up */ 9323431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 9333431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s", 9343431Scarlsonj lifr.lifr_name); 9353431Scarlsonj goto failure; 9363431Scarlsonj } 9373431Scarlsonj lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING; 9383431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 9393431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s", 9403431Scarlsonj lifr.lifr_name); 9413431Scarlsonj goto failure; 9423431Scarlsonj } 9433431Scarlsonj 9443431Scarlsonj /* Now we can create the internal LIF structure */ 9453431Scarlsonj hold_pif(pif); 9463431Scarlsonj if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL) 9473431Scarlsonj goto failure; 9483431Scarlsonj 9493431Scarlsonj dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf, 9503431Scarlsonj lif->lif_name); 9513431Scarlsonj lif->lif_plumbed = B_TRUE; 9523431Scarlsonj 9533431Scarlsonj return (lif); 9543431Scarlsonj 9553431Scarlsonj failure: 9563431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 9573431Scarlsonj errno != ENXIO) { 9583431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s", 9593431Scarlsonj lifr.lifr_name); 9603431Scarlsonj } 9613431Scarlsonj return (NULL); 9623431Scarlsonj } 9633431Scarlsonj 9643431Scarlsonj /* 9653431Scarlsonj * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used 9663431Scarlsonj * for all interfaces configured by DHCP (those in leases). 9673431Scarlsonj * 9683431Scarlsonj * input: dhcp_lif_t *: the interface to unplumb 9693431Scarlsonj * output: none 9703431Scarlsonj */ 9713431Scarlsonj 9723431Scarlsonj void 9733431Scarlsonj unplumb_lif(dhcp_lif_t *lif) 9743431Scarlsonj { 9753431Scarlsonj dhcp_lease_t *dlp; 9763431Scarlsonj 9773431Scarlsonj if (lif->lif_plumbed) { 9783431Scarlsonj struct lifreq lifr; 9793431Scarlsonj 9803431Scarlsonj (void) memset(&lifr, 0, sizeof (lifr)); 9813431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, 9823431Scarlsonj sizeof (lifr.lifr_name)); 9833431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 9843431Scarlsonj errno != ENXIO) { 9853431Scarlsonj dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s", 9863431Scarlsonj lif->lif_name); 9873431Scarlsonj } 9883431Scarlsonj lif->lif_plumbed = B_FALSE; 9893431Scarlsonj } 9905978Smeem 9913431Scarlsonj /* 9923431Scarlsonj * Special case: if we're "unplumbing" the main LIF for DHCPv4, then 9933431Scarlsonj * just canonize it and remove it from the lease. 9943431Scarlsonj */ 9953431Scarlsonj if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) { 9965381Smeem canonize_lif(lif, B_TRUE); 9973431Scarlsonj cancel_lif_timers(lif); 9983431Scarlsonj if (lif->lif_declined != NULL) { 9993431Scarlsonj dlp->dl_smach->dsm_lif_down--; 10003431Scarlsonj lif->lif_declined = NULL; 10013431Scarlsonj } 10023431Scarlsonj dlp->dl_nlifs = 0; 10033431Scarlsonj dlp->dl_lifs = NULL; 10043431Scarlsonj lif->lif_lease = NULL; 10053431Scarlsonj release_lif(lif); 10063431Scarlsonj } else { 10073431Scarlsonj remove_lif(lif); 10083431Scarlsonj } 10093431Scarlsonj } 10103431Scarlsonj 10113431Scarlsonj /* 10123431Scarlsonj * attach_lif(): create a new logical interface, creating the physical 10133431Scarlsonj * interface as necessary. 10143431Scarlsonj * 10153431Scarlsonj * input: const char *: the logical interface name 10163431Scarlsonj * boolean_t: B_TRUE for IPv6 10173431Scarlsonj * int *: set to DHCP_IPC_E_* if creation fails 10183431Scarlsonj * output: dhcp_lif_t *: pointer to new entry, or NULL on failure 10193431Scarlsonj */ 10203431Scarlsonj 10213431Scarlsonj dhcp_lif_t * 10223431Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error) 10233431Scarlsonj { 10243431Scarlsonj dhcp_pif_t *pif; 10253431Scarlsonj char pname[LIFNAMSIZ], *cp; 10263431Scarlsonj 10273431Scarlsonj (void) strlcpy(pname, lname, sizeof (pname)); 10283431Scarlsonj if ((cp = strchr(pname, ':')) != NULL) 10293431Scarlsonj *cp = '\0'; 10303431Scarlsonj 10313431Scarlsonj if ((pif = lookup_pif_by_name(pname, isv6)) != NULL) 10323431Scarlsonj hold_pif(pif); 10333431Scarlsonj else if ((pif = insert_pif(pname, isv6, error)) == NULL) 10343431Scarlsonj return (NULL); 10353431Scarlsonj 10363431Scarlsonj if (lookup_lif_by_name(lname, pif) != NULL) { 10373431Scarlsonj dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!", 10383431Scarlsonj lname); 10393431Scarlsonj release_pif(pif); 10403431Scarlsonj *error = DHCP_IPC_E_INVIF; 10413431Scarlsonj return (NULL); 10423431Scarlsonj } 10433431Scarlsonj 10443431Scarlsonj /* If LIF creation fails, then insert_lif discards our PIF hold */ 10453431Scarlsonj return (insert_lif(pif, lname, error)); 10463431Scarlsonj } 10473431Scarlsonj 10483431Scarlsonj /* 10493431Scarlsonj * set_lif_dhcp(): Set logical interface flags to show that it's managed 10503431Scarlsonj * by DHCP. 10513431Scarlsonj * 10523431Scarlsonj * input: dhcp_lif_t *: the logical interface 10533431Scarlsonj * boolean_t: B_TRUE if adopting 10543431Scarlsonj * output: int: set to DHCP_IPC_E_* if operation fails 10553431Scarlsonj */ 10563431Scarlsonj 10573431Scarlsonj int 10583431Scarlsonj set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting) 10593431Scarlsonj { 10603431Scarlsonj int fd; 10613431Scarlsonj int err; 10623431Scarlsonj struct lifreq lifr; 10633431Scarlsonj 10643431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 10653431Scarlsonj 10663431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 10673431Scarlsonj 10683431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 10693431Scarlsonj err = errno; 10703431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s", 10713431Scarlsonj lif->lif_name); 10723431Scarlsonj return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT); 10733431Scarlsonj } 10743431Scarlsonj lif->lif_flags = lifr.lifr_flags; 10753431Scarlsonj 10763431Scarlsonj /* 10773431Scarlsonj * Check for conflicting sources of address control, and other 10783431Scarlsonj * unacceptable configurations. 10793431Scarlsonj */ 10804823Sseb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 10814823Sseb IFF_VIRTUAL)) { 10823431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx", 10833431Scarlsonj lif->lif_name, lifr.lifr_flags); 10843431Scarlsonj return (DHCP_IPC_E_INVIF); 10853431Scarlsonj } 10863431Scarlsonj 10870Sstevel@tonic-gate /* 10880Sstevel@tonic-gate * if DHCPRUNNING is already set on the interface and we're 10890Sstevel@tonic-gate * not adopting it, the agent probably crashed and burned. 10900Sstevel@tonic-gate * note it, but don't let it stop the proceedings. we're 10910Sstevel@tonic-gate * pretty sure we're not already running, since we wouldn't 10920Sstevel@tonic-gate * have been able to bind to our IPC port. 10930Sstevel@tonic-gate */ 10940Sstevel@tonic-gate 10953431Scarlsonj if (lifr.lifr_flags & IFF_DHCPRUNNING) { 10963431Scarlsonj if (!is_adopting) { 10973431Scarlsonj dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already " 10983431Scarlsonj "set on %s", lif->lif_name); 10993431Scarlsonj } 11003431Scarlsonj } else { 11013431Scarlsonj lifr.lifr_flags |= IFF_DHCPRUNNING; 11023431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 11033431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s", 11043431Scarlsonj lif->lif_name); 11053431Scarlsonj return (DHCP_IPC_E_INT); 11063431Scarlsonj } 11073431Scarlsonj lif->lif_flags = lifr.lifr_flags; 11080Sstevel@tonic-gate } 11093431Scarlsonj return (DHCP_IPC_SUCCESS); 11103431Scarlsonj } 11110Sstevel@tonic-gate 11123431Scarlsonj /* 11133431Scarlsonj * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer 11143431Scarlsonj * managed by DHCP. 11153431Scarlsonj * 11163431Scarlsonj * input: dhcp_lif_t *: the logical interface 11173431Scarlsonj * output: none 11183431Scarlsonj */ 11190Sstevel@tonic-gate 11203431Scarlsonj static void 11213431Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif) 11223431Scarlsonj { 11233431Scarlsonj int fd; 11243431Scarlsonj struct lifreq lifr; 11250Sstevel@tonic-gate 11263431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 11273431Scarlsonj 11283431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 11293431Scarlsonj 11303431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 11313431Scarlsonj return; 11320Sstevel@tonic-gate 11333431Scarlsonj if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) 11343431Scarlsonj return; 11350Sstevel@tonic-gate 11363431Scarlsonj lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING; 11373431Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 11383431Scarlsonj } 11390Sstevel@tonic-gate 11403431Scarlsonj /* 11413431Scarlsonj * set_lif_deprecated(): Set the "deprecated" flag to tell users that this 11423431Scarlsonj * address will be going away. As the interface is 11433431Scarlsonj * going away, we don't care if there are errors. 11443431Scarlsonj * 11453431Scarlsonj * input: dhcp_lif_t *: the logical interface 11463431Scarlsonj * output: none 11473431Scarlsonj */ 11480Sstevel@tonic-gate 11493431Scarlsonj void 11503431Scarlsonj set_lif_deprecated(dhcp_lif_t *lif) 11513431Scarlsonj { 11523431Scarlsonj int fd; 11533431Scarlsonj struct lifreq lifr; 11540Sstevel@tonic-gate 11553431Scarlsonj if (lif->lif_flags & IFF_DEPRECATED) 11563431Scarlsonj return; 11570Sstevel@tonic-gate 11583431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 11593431Scarlsonj 11603431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 11610Sstevel@tonic-gate 11623431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 11633431Scarlsonj return; 11643431Scarlsonj 11653431Scarlsonj if (lifr.lifr_flags & IFF_DEPRECATED) 11663431Scarlsonj return; 11670Sstevel@tonic-gate 11683431Scarlsonj lifr.lifr_flags |= IFF_DEPRECATED; 11693431Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 11703431Scarlsonj lif->lif_flags = lifr.lifr_flags; 11713431Scarlsonj } 11723431Scarlsonj 11733431Scarlsonj /* 11743431Scarlsonj * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this 11753431Scarlsonj * address will not be going away. This happens if we 11763431Scarlsonj * get a renewal after preferred lifetime but before 11773431Scarlsonj * the valid lifetime. 11783431Scarlsonj * 11793431Scarlsonj * input: dhcp_lif_t *: the logical interface 11803431Scarlsonj * output: boolean_t: B_TRUE on success. 11813431Scarlsonj */ 11820Sstevel@tonic-gate 11833431Scarlsonj boolean_t 11843431Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif) 11853431Scarlsonj { 11863431Scarlsonj int fd; 11873431Scarlsonj struct lifreq lifr; 11883431Scarlsonj 11893431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 11903431Scarlsonj 11913431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 11923431Scarlsonj 11933431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 11943431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s", 11953431Scarlsonj lif->lif_name); 11963431Scarlsonj return (B_FALSE); 11970Sstevel@tonic-gate } 11980Sstevel@tonic-gate 11990Sstevel@tonic-gate /* 12003431Scarlsonj * Check for conflicting sources of address control, and other 12013431Scarlsonj * unacceptable configurations. 12020Sstevel@tonic-gate */ 12034823Sseb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 12044823Sseb IFF_VIRTUAL)) { 12053431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags " 12063431Scarlsonj "are %llx", lif->lif_name, lifr.lifr_flags); 12073431Scarlsonj return (B_FALSE); 12080Sstevel@tonic-gate } 12090Sstevel@tonic-gate 12103431Scarlsonj if (!(lifr.lifr_flags & IFF_DEPRECATED)) 12113431Scarlsonj return (B_TRUE); 12120Sstevel@tonic-gate 12133431Scarlsonj lifr.lifr_flags &= ~IFF_DEPRECATED; 12143431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 12153431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s", 12163431Scarlsonj lif->lif_name); 12173431Scarlsonj return (B_FALSE); 12183431Scarlsonj } else { 12193431Scarlsonj lif->lif_flags = lifr.lifr_flags; 12203431Scarlsonj return (B_TRUE); 12210Sstevel@tonic-gate } 12220Sstevel@tonic-gate } 12230Sstevel@tonic-gate 12240Sstevel@tonic-gate /* 12253431Scarlsonj * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only). 12260Sstevel@tonic-gate * 12273431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 12285381Smeem * in_addr_t: the address the socket will be bound to (in hbo) 12293431Scarlsonj * output: boolean_t: B_TRUE if the socket was opened successfully. 12300Sstevel@tonic-gate */ 12310Sstevel@tonic-gate 12323431Scarlsonj boolean_t 12335381Smeem open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo) 12340Sstevel@tonic-gate { 12355381Smeem const char *errmsg; 12365381Smeem struct lifreq lifr; 12375381Smeem int on = 1; 12385455Smeem uchar_t ttl = 255; 12395381Smeem 12403431Scarlsonj if (lif->lif_sock_ip_fd != -1) { 12413431Scarlsonj dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s", 12423431Scarlsonj lif->lif_name); 12433431Scarlsonj return (B_FALSE); 12440Sstevel@tonic-gate } 12450Sstevel@tonic-gate 12463431Scarlsonj lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0); 12473431Scarlsonj if (lif->lif_sock_ip_fd == -1) { 12485381Smeem errmsg = "cannot create v4 socket"; 12495381Smeem goto failure; 12505381Smeem } 12515381Smeem 12525381Smeem if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) { 12535381Smeem errmsg = "cannot bind v4 socket"; 12545381Smeem goto failure; 12555381Smeem } 12565381Smeem 12575381Smeem /* 12585381Smeem * If we bound to INADDR_ANY, we have no IFF_UP source address to use. 12595381Smeem * Thus, enable IP_UNSPEC_SRC so that we can send packets with an 12605381Smeem * unspecified (0.0.0.0) address. Also, enable IP_DHCPINIT_IF so that 12615381Smeem * the IP module will accept unicast DHCP traffic regardless of the IP 12625381Smeem * address it's sent to. (We'll then figure out which packets are 12635381Smeem * ours based on the xid.) 12645381Smeem */ 12655381Smeem if (addr_hbo == INADDR_ANY) { 12665381Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC, 12675381Smeem &on, sizeof (int)) == -1) { 12685381Smeem errmsg = "cannot set IP_UNSPEC_SRC"; 12695381Smeem goto failure; 12705381Smeem } 12715381Smeem 12725381Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF, 12735381Smeem &lif->lif_pif->pif_index, sizeof (int)) == -1) { 12745381Smeem errmsg = "cannot set IP_DHCPINIT_IF"; 12755381Smeem goto failure; 12765381Smeem } 12775381Smeem } 12785381Smeem 12795455Smeem /* 12805455Smeem * Unfortunately, some hardware (such as the Linksys WRT54GC) 12815455Smeem * decrements the TTL *prior* to accepting DHCP traffic destined 12825455Smeem * for it. To workaround this, tell IP to use a TTL of 255 for 12835455Smeem * broadcast packets sent from this socket. 12845455Smeem */ 12855455Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BROADCAST_TTL, &ttl, 12865455Smeem sizeof (uchar_t)) == -1) { 12875455Smeem errmsg = "cannot set IP_BROADCAST_TTL"; 12885455Smeem goto failure; 12895455Smeem } 12905455Smeem 12915381Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, 12925381Smeem &lif->lif_pif->pif_index, sizeof (int)) == -1) { 12935381Smeem errmsg = "cannot set IP_BOUND_IF"; 12945381Smeem goto failure; 12950Sstevel@tonic-gate } 12960Sstevel@tonic-gate 12975381Smeem /* 12985381Smeem * Make sure at least one lif on the interface we used in IP_BOUND_IF 12995381Smeem * is IFF_UP so that we can send and receive IP packets. 13005381Smeem */ 13015381Smeem (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 13025381Smeem if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 13035381Smeem errmsg = "cannot get interface flags"; 13045381Smeem goto failure; 13053431Scarlsonj } 13060Sstevel@tonic-gate 13075381Smeem if (!(lifr.lifr_flags & IFF_UP)) { 13085381Smeem /* 13095381Smeem * Start from a clean slate. 13105381Smeem */ 13115381Smeem canonize_lif(lif, B_FALSE); 13125381Smeem 13135381Smeem lifr.lifr_flags |= IFF_UP; 13145381Smeem if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 13155381Smeem errmsg = "cannot bring up"; 13165381Smeem goto failure; 13175381Smeem } 13185439Smeem lif->lif_flags = lifr.lifr_flags; 13195381Smeem 13205381Smeem /* 13215381Smeem * When bringing 0.0.0.0 IFF_UP, the kernel changes the 13225381Smeem * netmask to 255.0.0.0, so re-fetch our expected netmask. 13235381Smeem */ 13245381Smeem if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) { 13255381Smeem errmsg = "cannot get netmask"; 13265381Smeem goto failure; 13275381Smeem } 13285381Smeem 13295381Smeem lif->lif_netmask = 13305381Smeem ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr; 13310Sstevel@tonic-gate } 13325381Smeem 13335381Smeem lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN, 13345381Smeem dhcp_packet_lif, lif); 13355381Smeem if (lif->lif_packet_id == -1) { 13365381Smeem errmsg = "cannot register to receive DHCP packets"; 13375381Smeem goto failure; 13385381Smeem } 13395381Smeem 13400Sstevel@tonic-gate return (B_TRUE); 13415381Smeem failure: 13425381Smeem dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg); 13435381Smeem close_ip_lif(lif); 13445381Smeem return (B_FALSE); 13450Sstevel@tonic-gate } 13460Sstevel@tonic-gate 13470Sstevel@tonic-gate /* 13483431Scarlsonj * close_ip_lif(): close an IP socket for I/O on a given LIF. 13490Sstevel@tonic-gate * 13503431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 13513431Scarlsonj * output: none 13520Sstevel@tonic-gate */ 13530Sstevel@tonic-gate 13543431Scarlsonj void 13553431Scarlsonj close_ip_lif(dhcp_lif_t *lif) 13560Sstevel@tonic-gate { 13575381Smeem if (lif->lif_packet_id != -1) { 13585381Smeem (void) iu_unregister_event(eh, lif->lif_packet_id, NULL); 13595381Smeem lif->lif_packet_id = -1; 13603431Scarlsonj } 13613431Scarlsonj if (lif->lif_sock_ip_fd != -1) { 13623431Scarlsonj (void) close(lif->lif_sock_ip_fd); 13633431Scarlsonj lif->lif_sock_ip_fd = -1; 13643431Scarlsonj } 13653431Scarlsonj } 13660Sstevel@tonic-gate 13673431Scarlsonj /* 13683431Scarlsonj * lif_mark_decline(): mark a LIF as having been declined due to a duplicate 13693431Scarlsonj * address or some other conflict. This is used in 13703431Scarlsonj * send_declines() to report failure back to the server. 13713431Scarlsonj * 13723431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 13733431Scarlsonj * const char *: text string explaining why the address is declined 13743431Scarlsonj * output: none 13753431Scarlsonj */ 13760Sstevel@tonic-gate 13773431Scarlsonj void 13783431Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason) 13793431Scarlsonj { 13803431Scarlsonj if (lif->lif_declined == NULL) { 13813431Scarlsonj dhcp_lease_t *dlp; 13820Sstevel@tonic-gate 13833431Scarlsonj lif->lif_declined = reason; 13843431Scarlsonj if ((dlp = lif->lif_lease) != NULL) 13853431Scarlsonj dlp->dl_smach->dsm_lif_down++; 13860Sstevel@tonic-gate } 13870Sstevel@tonic-gate } 13880Sstevel@tonic-gate 13890Sstevel@tonic-gate /* 13903431Scarlsonj * schedule_lif_timer(): schedules the LIF-related timer 13910Sstevel@tonic-gate * 13923431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 13933431Scarlsonj * dhcp_timer_t *: the timer to schedule 13943431Scarlsonj * iu_tq_callback_t *: the callback to call upon firing 13953431Scarlsonj * output: boolean_t: B_TRUE if the timer was scheduled successfully 13960Sstevel@tonic-gate */ 13970Sstevel@tonic-gate 13983431Scarlsonj boolean_t 13993431Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire) 14000Sstevel@tonic-gate { 14010Sstevel@tonic-gate /* 14023431Scarlsonj * If there's a timer running, cancel it and release its lease 14033431Scarlsonj * reference. 14040Sstevel@tonic-gate */ 14053431Scarlsonj if (dt->dt_id != -1) { 14063431Scarlsonj if (!cancel_timer(dt)) 14073431Scarlsonj return (B_FALSE); 14083431Scarlsonj release_lif(lif); 14093431Scarlsonj } 14100Sstevel@tonic-gate 14113431Scarlsonj if (schedule_timer(dt, expire, lif)) { 14123431Scarlsonj hold_lif(lif); 14133431Scarlsonj return (B_TRUE); 14143431Scarlsonj } else { 14153431Scarlsonj dhcpmsg(MSG_WARNING, 14163431Scarlsonj "schedule_lif_timer: cannot schedule timer"); 14173431Scarlsonj return (B_FALSE); 14183431Scarlsonj } 14190Sstevel@tonic-gate } 14200Sstevel@tonic-gate 14210Sstevel@tonic-gate /* 14223431Scarlsonj * cancel_lif_timer(): cancels a LIF-related timer 14230Sstevel@tonic-gate * 14243431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 14253431Scarlsonj * dhcp_timer_t *: the timer to cancel 14263431Scarlsonj * output: none 14270Sstevel@tonic-gate */ 14280Sstevel@tonic-gate 14293431Scarlsonj static void 14303431Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt) 14310Sstevel@tonic-gate { 14323431Scarlsonj if (dt->dt_id == -1) 14333431Scarlsonj return; 14343431Scarlsonj if (cancel_timer(dt)) { 14353431Scarlsonj dhcpmsg(MSG_DEBUG2, 14363431Scarlsonj "cancel_lif_timer: canceled expiry timer on %s", 14373431Scarlsonj lif->lif_name); 14383431Scarlsonj release_lif(lif); 14393431Scarlsonj } else { 14403431Scarlsonj dhcpmsg(MSG_WARNING, 14413431Scarlsonj "cancel_lif_timer: cannot cancel timer on %s", 14423431Scarlsonj lif->lif_name); 14430Sstevel@tonic-gate } 14440Sstevel@tonic-gate } 14450Sstevel@tonic-gate 14460Sstevel@tonic-gate /* 14473431Scarlsonj * cancel_lif_timers(): cancels the LIF-related timers 14480Sstevel@tonic-gate * 14493431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 14503431Scarlsonj * output: none 14510Sstevel@tonic-gate */ 14520Sstevel@tonic-gate 14530Sstevel@tonic-gate void 14543431Scarlsonj cancel_lif_timers(dhcp_lif_t *lif) 14550Sstevel@tonic-gate { 14563431Scarlsonj cancel_lif_timer(lif, &lif->lif_preferred); 14573431Scarlsonj cancel_lif_timer(lif, &lif->lif_expire); 14580Sstevel@tonic-gate } 14590Sstevel@tonic-gate 14600Sstevel@tonic-gate /* 14613431Scarlsonj * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common 14623431Scarlsonj * file descriptors (v4_sock_fd and v6_sock_fd). 14630Sstevel@tonic-gate * 14643431Scarlsonj * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4 14653431Scarlsonj * output: none 14660Sstevel@tonic-gate */ 14670Sstevel@tonic-gate 14683431Scarlsonj uint_t 14693431Scarlsonj get_max_mtu(boolean_t isv6) 14700Sstevel@tonic-gate { 14713431Scarlsonj uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu; 14723431Scarlsonj 14733431Scarlsonj if (*mtup == 0) { 14743431Scarlsonj dhcp_pif_t *pif; 14753431Scarlsonj dhcp_lif_t *lif; 14763431Scarlsonj struct lifreq lifr; 14773431Scarlsonj 14783431Scarlsonj /* Set an arbitrary lower bound */ 14793431Scarlsonj *mtup = 1024; 14803431Scarlsonj pif = isv6 ? v6root : v4root; 14813431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 14823431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; 14833431Scarlsonj lif = lif->lif_next) { 14843431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, 14853431Scarlsonj LIFNAMSIZ); 14863431Scarlsonj if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) != 14873431Scarlsonj -1 && lifr.lifr_mtu > *mtup) { 14883431Scarlsonj *mtup = lifr.lifr_mtu; 14893431Scarlsonj } 14903431Scarlsonj } 14913431Scarlsonj } 14920Sstevel@tonic-gate } 14933431Scarlsonj return (*mtup); 14940Sstevel@tonic-gate } 14950Sstevel@tonic-gate 14960Sstevel@tonic-gate /* 14973431Scarlsonj * expired_lif_state(): summarize the state of expired LIFs on a given state 14983431Scarlsonj * machine. 14990Sstevel@tonic-gate * 15003431Scarlsonj * input: dhcp_smach_t *: the state machine to scan 15013431Scarlsonj * output: dhcp_expire_t: overall state 15020Sstevel@tonic-gate */ 15030Sstevel@tonic-gate 15043431Scarlsonj dhcp_expire_t 15053431Scarlsonj expired_lif_state(dhcp_smach_t *dsmp) 15060Sstevel@tonic-gate { 15073431Scarlsonj dhcp_lease_t *dlp; 15083431Scarlsonj dhcp_lif_t *lif; 15093431Scarlsonj uint_t nlifs; 15103431Scarlsonj uint_t numlifs; 15113431Scarlsonj uint_t numexp; 15120Sstevel@tonic-gate 15133431Scarlsonj numlifs = numexp = 0; 15143431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 15153431Scarlsonj lif = dlp->dl_lifs; 15163431Scarlsonj nlifs = dlp->dl_nlifs; 15173431Scarlsonj numlifs += nlifs; 15183431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 15193431Scarlsonj if (lif->lif_expired) 15203431Scarlsonj numexp++; 15213431Scarlsonj } 15220Sstevel@tonic-gate } 15233431Scarlsonj if (numlifs == 0) 15243431Scarlsonj return (DHCP_EXP_NOLIFS); 15253431Scarlsonj else if (numexp == 0) 15263431Scarlsonj return (DHCP_EXP_NOEXP); 15273431Scarlsonj else if (numlifs == numexp) 15283431Scarlsonj return (DHCP_EXP_ALLEXP); 15293431Scarlsonj else 15303431Scarlsonj return (DHCP_EXP_SOMEEXP); 15310Sstevel@tonic-gate } 15320Sstevel@tonic-gate 15330Sstevel@tonic-gate /* 15343431Scarlsonj * find_expired_lif(): find the first expired LIF on a given state machine 15350Sstevel@tonic-gate * 15363431Scarlsonj * input: dhcp_smach_t *: the state machine to scan 15373431Scarlsonj * output: dhcp_lif_t *: the first expired LIF, or NULL if none. 15380Sstevel@tonic-gate */ 15390Sstevel@tonic-gate 15403431Scarlsonj dhcp_lif_t * 15413431Scarlsonj find_expired_lif(dhcp_smach_t *dsmp) 15420Sstevel@tonic-gate { 15433431Scarlsonj dhcp_lease_t *dlp; 15443431Scarlsonj dhcp_lif_t *lif; 15453431Scarlsonj uint_t nlifs; 15463431Scarlsonj 15473431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 15483431Scarlsonj lif = dlp->dl_lifs; 15493431Scarlsonj nlifs = dlp->dl_nlifs; 15503431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 15513431Scarlsonj if (lif->lif_expired) 15523431Scarlsonj return (lif); 15533431Scarlsonj } 15543431Scarlsonj } 15553431Scarlsonj return (NULL); 15563431Scarlsonj } 15573431Scarlsonj 15583431Scarlsonj /* 15593431Scarlsonj * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used 15603431Scarlsonj * only for DHCPv6. 15613431Scarlsonj * 15623431Scarlsonj * input: none 15633431Scarlsonj * output: none 15643431Scarlsonj */ 15653431Scarlsonj 15663431Scarlsonj void 15673431Scarlsonj remove_v6_strays(void) 15683431Scarlsonj { 15693431Scarlsonj struct lifnum lifn; 15703431Scarlsonj struct lifconf lifc; 15713431Scarlsonj struct lifreq *lifrp, *lifrmax; 15723431Scarlsonj uint_t numifs; 15733431Scarlsonj uint64_t flags; 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate /* 15763431Scarlsonj * Get the approximate number of interfaces in the system. It's only 15773431Scarlsonj * approximate because the system is dynamic -- interfaces may be 15783431Scarlsonj * plumbed or unplumbed at any time. This is also the reason for the 15793431Scarlsonj * "+ 10" fudge factor: we're trying to avoid unnecessary looping. 15800Sstevel@tonic-gate */ 15813431Scarlsonj (void) memset(&lifn, 0, sizeof (lifn)); 15823431Scarlsonj lifn.lifn_family = AF_INET6; 15833431Scarlsonj lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 15843431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) { 15853431Scarlsonj dhcpmsg(MSG_ERR, 15863431Scarlsonj "remove_v6_strays: cannot read number of interfaces"); 15873431Scarlsonj numifs = 10; 15883431Scarlsonj } else { 15893431Scarlsonj numifs = lifn.lifn_count + 10; 15900Sstevel@tonic-gate } 15910Sstevel@tonic-gate 15920Sstevel@tonic-gate /* 15933431Scarlsonj * Get the interface information. We do this in a loop so that we can 15943431Scarlsonj * recover from EINVAL from the kernel -- delivered when the buffer is 15953431Scarlsonj * too small. 15960Sstevel@tonic-gate */ 15973431Scarlsonj (void) memset(&lifc, 0, sizeof (lifc)); 15983431Scarlsonj lifc.lifc_family = AF_INET6; 15993431Scarlsonj lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 16003431Scarlsonj for (;;) { 16013431Scarlsonj lifc.lifc_len = numifs * sizeof (*lifrp); 16023431Scarlsonj lifrp = realloc(lifc.lifc_buf, lifc.lifc_len); 16033431Scarlsonj if (lifrp == NULL) { 16043431Scarlsonj dhcpmsg(MSG_ERR, 16053431Scarlsonj "remove_v6_strays: cannot allocate memory"); 16063431Scarlsonj free(lifc.lifc_buf); 16073431Scarlsonj return; 16080Sstevel@tonic-gate } 16093431Scarlsonj lifc.lifc_buf = (caddr_t)lifrp; 16103431Scarlsonj errno = 0; 16113431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 && 16123431Scarlsonj lifc.lifc_len < numifs * sizeof (*lifrp)) 16130Sstevel@tonic-gate break; 16143431Scarlsonj if (errno == 0 || errno == EINVAL) { 16153431Scarlsonj numifs <<= 1; 16163431Scarlsonj } else { 16173431Scarlsonj dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF"); 16183431Scarlsonj free(lifc.lifc_buf); 16193431Scarlsonj return; 16200Sstevel@tonic-gate } 16210Sstevel@tonic-gate } 16220Sstevel@tonic-gate 16233431Scarlsonj lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp); 16243431Scarlsonj for (; lifrp < lifrmax; lifrp++) { 16250Sstevel@tonic-gate /* 16263431Scarlsonj * Get the interface flags; we're interested in the DHCP ones. 16270Sstevel@tonic-gate */ 16283431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1) 16293431Scarlsonj continue; 16303431Scarlsonj flags = lifrp->lifr_flags; 16313431Scarlsonj if (!(flags & IFF_DHCPRUNNING)) 16323431Scarlsonj continue; 16330Sstevel@tonic-gate /* 16343431Scarlsonj * If the interface has a link-local address, then we don't 16353431Scarlsonj * control it. Just remove the flag. 16360Sstevel@tonic-gate */ 16373431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1) 16383431Scarlsonj continue; 16393431Scarlsonj if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp-> 16403431Scarlsonj lifr_addr)->sin6_addr)) { 16413431Scarlsonj lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING; 16423431Scarlsonj (void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp); 16433431Scarlsonj continue; 16440Sstevel@tonic-gate } 16450Sstevel@tonic-gate /* 16463431Scarlsonj * All others are (or were) under our control. Clean up by 16473431Scarlsonj * removing them. 16480Sstevel@tonic-gate */ 16493431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) { 16503431Scarlsonj dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s", 16513431Scarlsonj lifrp->lifr_name); 16523431Scarlsonj } else if (errno != ENXIO) { 16533431Scarlsonj dhcpmsg(MSG_ERR, 16543431Scarlsonj "remove_v6_strays: SIOCLIFREMOVEIF %s", 16553431Scarlsonj lifrp->lifr_name); 16560Sstevel@tonic-gate } 16570Sstevel@tonic-gate } 16583431Scarlsonj free(lifc.lifc_buf); 16590Sstevel@tonic-gate } 1660