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 /* 223431Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <sys/socket.h> 300Sstevel@tonic-gate #include <net/if.h> 310Sstevel@tonic-gate #include <stdlib.h> 320Sstevel@tonic-gate #include <sys/sockio.h> 330Sstevel@tonic-gate #include <netinet/in.h> 340Sstevel@tonic-gate #include <netinet/dhcp.h> 350Sstevel@tonic-gate #include <string.h> 360Sstevel@tonic-gate #include <unistd.h> 373431Scarlsonj #include <search.h> 383431Scarlsonj #include <libdevinfo.h> 394456Sss150715 #include <libdlpi.h> 400Sstevel@tonic-gate #include <netinet/if_ether.h> 413431Scarlsonj #include <arpa/inet.h> 420Sstevel@tonic-gate #include <dhcpmsg.h> 433431Scarlsonj #include <dhcp_inittab.h> 444456Sss150715 #include <stropts.h> 450Sstevel@tonic-gate 463431Scarlsonj #include "agent.h" 470Sstevel@tonic-gate #include "interface.h" 480Sstevel@tonic-gate #include "util.h" 490Sstevel@tonic-gate #include "dlpi_io.h" 500Sstevel@tonic-gate #include "packet.h" 510Sstevel@tonic-gate #include "states.h" 523431Scarlsonj 533431Scarlsonj dhcp_pif_t *v4root; 543431Scarlsonj dhcp_pif_t *v6root; 553431Scarlsonj 563431Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu; 570Sstevel@tonic-gate 580Sstevel@tonic-gate /* 593431Scarlsonj * Interface flags to watch: things that should be under our direct control. 600Sstevel@tonic-gate */ 61*4823Sseb #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \ 62*4823Sseb IFF_TEMPORARY) 630Sstevel@tonic-gate 643431Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *); 650Sstevel@tonic-gate 660Sstevel@tonic-gate /* 673431Scarlsonj * insert_pif(): creates a new physical interface structure and chains it on 683431Scarlsonj * the list. Initializes state that remains consistent across 693431Scarlsonj * all use of the physical interface entry. 700Sstevel@tonic-gate * 713431Scarlsonj * input: const char *: the name of the physical interface 723431Scarlsonj * boolean_t: if B_TRUE, this is DHCPv6 733431Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 740Sstevel@tonic-gate * error code with the reason why 753431Scarlsonj * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure 760Sstevel@tonic-gate */ 770Sstevel@tonic-gate 783431Scarlsonj dhcp_pif_t * 793431Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error) 800Sstevel@tonic-gate { 813431Scarlsonj dhcp_pif_t *pif; 823431Scarlsonj struct lifreq lifr; 830Sstevel@tonic-gate 843431Scarlsonj if ((pif = calloc(1, sizeof (*pif))) == NULL) { 853431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for " 863431Scarlsonj "%s", pname); 873431Scarlsonj *error = DHCP_IPC_E_MEMORY; 880Sstevel@tonic-gate return (NULL); 890Sstevel@tonic-gate } 900Sstevel@tonic-gate 913431Scarlsonj pif->pif_isv6 = isv6; 924456Sss150715 pif->pif_dlpi_hd = NULL; 933431Scarlsonj pif->pif_dlpi_id = -1; 943431Scarlsonj pif->pif_hold_count = 1; 953431Scarlsonj pif->pif_running = B_TRUE; 963431Scarlsonj 973431Scarlsonj if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) { 983431Scarlsonj dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long", 993431Scarlsonj pname); 1003431Scarlsonj *error = DHCP_IPC_E_INVIF; 1013431Scarlsonj goto failure; 1023431Scarlsonj } 1033431Scarlsonj 1043431Scarlsonj /* We do not use DLPI with DHCPv6 */ 1053431Scarlsonj if (!isv6) { 1064456Sss150715 int rc; 1074456Sss150715 dlpi_handle_t dh; 1084456Sss150715 dlpi_info_t dlinfo; 1093431Scarlsonj 1103431Scarlsonj /* 1113431Scarlsonj * Do the allocations necessary for IPv4 DHCP. 1123431Scarlsonj * 1133431Scarlsonj * 1. open the interface using DLPI 1143431Scarlsonj * 2. get the interface max SDU 1153431Scarlsonj * 3. get the interface hardware type and hardware length 1163431Scarlsonj * 4. get the interface hardware address 1173431Scarlsonj * 5. get the interface hardware broadcast address 1183431Scarlsonj */ 1193431Scarlsonj 1203431Scarlsonj /* step 1 */ 1214456Sss150715 if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) { 1224456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s", 1234456Sss150715 dlpi_strerror(rc)); 1244456Sss150715 *error = DHCP_IPC_E_INVIF; 1254456Sss150715 goto failure; 1264456Sss150715 } 1274456Sss150715 pif->pif_dlpi_hd = dh; 1284456Sss150715 1294456Sss150715 if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) { 1304456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s", 1314456Sss150715 dlpi_strerror(rc)); 1323431Scarlsonj *error = DHCP_IPC_E_INVIF; 1333431Scarlsonj goto failure; 1343431Scarlsonj } 1353431Scarlsonj 1363431Scarlsonj /* step 2 */ 1374456Sss150715 rc = dlpi_info(pif->pif_dlpi_hd, &dlinfo, 0); 1384456Sss150715 if (rc != DLPI_SUCCESS) { 1394456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s", 1404456Sss150715 dlpi_strerror(rc)); 1414456Sss150715 *error = DHCP_IPC_E_INVIF; 1424456Sss150715 goto failure; 1434456Sss150715 } 1444456Sss150715 1454456Sss150715 pif->pif_max = dlinfo.di_max_sdu; 1463431Scarlsonj if (pif->pif_max < DHCP_DEF_MAX_SIZE) { 1473431Scarlsonj dhcpmsg(MSG_ERROR, "insert_pif: %s does not have a " 1483431Scarlsonj "large enough maximum SDU to support DHCP " 1493431Scarlsonj "(%u < %u)", pname, pif->pif_max, 1503431Scarlsonj DHCP_DEF_MAX_SIZE); 1513431Scarlsonj *error = DHCP_IPC_E_INVIF; 1523431Scarlsonj goto failure; 1533431Scarlsonj } 1543431Scarlsonj 1553431Scarlsonj /* step 3 */ 1564456Sss150715 pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype); 1574456Sss150715 pif->pif_hwlen = dlinfo.di_physaddrlen; 1583431Scarlsonj 1593431Scarlsonj dhcpmsg(MSG_DEBUG, "insert_pif: %s: sdumax %u, hwtype %d, " 1603431Scarlsonj "hwlen %d", pname, pif->pif_max, pif->pif_hwtype, 1613431Scarlsonj pif->pif_hwlen); 1623431Scarlsonj 1633431Scarlsonj /* step 4 */ 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 } 1723431Scarlsonj } 1733431Scarlsonj 1744456Sss150715 (void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr, 1754456Sss150715 pif->pif_hwlen); 1764456Sss150715 1773431Scarlsonj /* 1784456Sss150715 * step 5 1794456Sss150715 * Some media types has no broadcast address. 1803431Scarlsonj */ 1814456Sss150715 if ((pif->pif_dlen = dlinfo.di_bcastaddrlen) != 0) { 1824456Sss150715 pif->pif_daddr = malloc(pif->pif_dlen); 1834456Sss150715 if (pif->pif_daddr == NULL) { 1844456Sss150715 dhcpmsg(MSG_ERR, "insert_pif: cannot allocate " 1854456Sss150715 "pif_daddr for %s", pname); 1864456Sss150715 *error = DHCP_IPC_E_MEMORY; 1874456Sss150715 goto failure; 1884456Sss150715 } 1893431Scarlsonj } 1904456Sss150715 (void) memcpy(pif->pif_daddr, dlinfo.di_bcastaddr, 1914456Sss150715 pif->pif_dlen); 1923431Scarlsonj 1933431Scarlsonj /* Close the DLPI stream until actually needed */ 1943431Scarlsonj close_dlpi_pif(pif); 1953431Scarlsonj } 1963431Scarlsonj 1970Sstevel@tonic-gate /* 1983431Scarlsonj * This is a bit gross, but IP has a confused interface. We must 1993431Scarlsonj * assume that the zeroth LIF is plumbed, and must query there to get 2003431Scarlsonj * the interface index number. 2010Sstevel@tonic-gate */ 2023431Scarlsonj (void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ); 2030Sstevel@tonic-gate 2043431Scarlsonj if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFINDEX, &lifr) == -1) { 2053431Scarlsonj if (errno == ENXIO) 2063431Scarlsonj *error = DHCP_IPC_E_INVIF; 2073431Scarlsonj else 2083431Scarlsonj *error = DHCP_IPC_E_INT; 2093431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname); 2103431Scarlsonj goto failure; 2113431Scarlsonj } 2123431Scarlsonj pif->pif_index = lifr.lifr_index; 2133431Scarlsonj 2143431Scarlsonj insque(pif, isv6 ? &v6root : &v4root); 2153431Scarlsonj 2163431Scarlsonj return (pif); 2173431Scarlsonj 2183431Scarlsonj failure: 2193431Scarlsonj release_pif(pif); 2203431Scarlsonj return (NULL); 2213431Scarlsonj } 2223431Scarlsonj 2233431Scarlsonj /* 2243431Scarlsonj * hold_pif(): acquire a hold on a physical interface structure. 2253431Scarlsonj * 2263431Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure 2273431Scarlsonj * output: none 2283431Scarlsonj */ 2293431Scarlsonj 2303431Scarlsonj void 2313431Scarlsonj hold_pif(dhcp_pif_t *pif) 2323431Scarlsonj { 2333431Scarlsonj pif->pif_hold_count++; 2343431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name, 2353431Scarlsonj pif->pif_hold_count); 2363431Scarlsonj } 2373431Scarlsonj 2383431Scarlsonj /* 2393431Scarlsonj * release_pif(): release a hold on a physical interface structure; will 2403431Scarlsonj * destroy the structure on the last hold removed. 2413431Scarlsonj * 2423431Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure 2433431Scarlsonj * output: none 2443431Scarlsonj */ 2453431Scarlsonj 2463431Scarlsonj void 2473431Scarlsonj release_pif(dhcp_pif_t *pif) 2483431Scarlsonj { 2493431Scarlsonj if (pif->pif_hold_count == 0) { 2503431Scarlsonj dhcpmsg(MSG_CRIT, "release_pif: extraneous release"); 2513431Scarlsonj return; 2523431Scarlsonj } 2533431Scarlsonj 2543431Scarlsonj if (--pif->pif_hold_count == 0) { 2553431Scarlsonj dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s", 2563431Scarlsonj pif->pif_name); 2573431Scarlsonj 2583431Scarlsonj remque(pif); 2593431Scarlsonj pif->pif_dlpi_count = 1; 2603431Scarlsonj close_dlpi_pif(pif); 2613431Scarlsonj free(pif->pif_hwaddr); 2623431Scarlsonj free(pif->pif_daddr); 2633431Scarlsonj free(pif); 2643431Scarlsonj } else { 2653431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u", 2663431Scarlsonj pif->pif_name, pif->pif_hold_count); 2673431Scarlsonj } 2683431Scarlsonj } 2693431Scarlsonj 2703431Scarlsonj /* 2713431Scarlsonj * lookup_pif_by_index(): Looks up PIF entries given regular ifIndex. 2723431Scarlsonj * 2733431Scarlsonj * input: uint_t: the interface index 2743431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 2753431Scarlsonj * output: dhcp_pif_t *: the matching PIF, or NULL if not found 2763431Scarlsonj */ 2773431Scarlsonj 2783431Scarlsonj dhcp_pif_t * 2793431Scarlsonj lookup_pif_by_index(uint_t ifindex, boolean_t isv6) 2803431Scarlsonj { 2813431Scarlsonj dhcp_pif_t *pif; 2823431Scarlsonj 2833431Scarlsonj for (pif = isv6 ? v6root : v4root; pif != NULL; pif = pif->pif_next) { 2843431Scarlsonj if (pif->pif_index == ifindex) 2853431Scarlsonj break; 2863431Scarlsonj } 2873431Scarlsonj 2883431Scarlsonj return (pif); 2893431Scarlsonj } 2903431Scarlsonj 2913431Scarlsonj /* 2923431Scarlsonj * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and 2933431Scarlsonj * previous PIF pointer (or NULL for list start). 2943431Scarlsonj * Caller is expected to iterate through all 2953431Scarlsonj * potential matches to find interface of interest. 2963431Scarlsonj * 2973431Scarlsonj * input: uint16_t: the interface index (truncated) 2983431Scarlsonj * dhcp_pif_t *: the previous PIF, or NULL for list start 2993431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 3003431Scarlsonj * output: dhcp_pif_t *: the next matching PIF, or NULL if not found 3013431Scarlsonj * note: This operates using the 'truncated' (16-bit) ifindex as seen by 3023431Scarlsonj * routing socket clients. The value stored in pif_index is the 3033431Scarlsonj * 32-bit ifindex from the ioctl interface. 3043431Scarlsonj */ 3053431Scarlsonj 3063431Scarlsonj dhcp_pif_t * 3073431Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6) 3083431Scarlsonj { 3093431Scarlsonj if (pif == NULL) 3103431Scarlsonj pif = isv6 ? v6root : v4root; 3113431Scarlsonj else 3123431Scarlsonj pif = pif->pif_next; 3133431Scarlsonj 3143431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 3153431Scarlsonj if ((pif->pif_index & 0xffff) == ifindex) 3163431Scarlsonj break; 3173431Scarlsonj } 3183431Scarlsonj 3193431Scarlsonj return (pif); 3203431Scarlsonj } 3213431Scarlsonj 3223431Scarlsonj /* 3233431Scarlsonj * lookup_pif_by_name(): Looks up a physical interface entry given a name. 3243431Scarlsonj * 3253431Scarlsonj * input: const char *: the physical interface name 3263431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 3273431Scarlsonj * output: dhcp_pif_t *: the matching PIF, or NULL if not found 3283431Scarlsonj */ 3293431Scarlsonj 3303431Scarlsonj dhcp_pif_t * 3313431Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6) 3323431Scarlsonj { 3333431Scarlsonj dhcp_pif_t *pif; 3343431Scarlsonj 3353431Scarlsonj pif = isv6 ? v6root : v4root; 3363431Scarlsonj 3373431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 3383431Scarlsonj if (strcmp(pif->pif_name, pname) == 0) 3393431Scarlsonj break; 3403431Scarlsonj } 3413431Scarlsonj 3423431Scarlsonj return (pif); 3433431Scarlsonj } 3443431Scarlsonj 3453431Scarlsonj /* 3463431Scarlsonj * open_dlpi_pif(): register the use of DLPI I/O by a LIF on a PIF, opening 3473431Scarlsonj * the connection if necessary. 3483431Scarlsonj * 3493431Scarlsonj * input: dhcp_pif_t *: the physical interface on which to use DLPI 3503431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure. 3513431Scarlsonj */ 3523431Scarlsonj 3533431Scarlsonj boolean_t 3543431Scarlsonj open_dlpi_pif(dhcp_pif_t *pif) 3553431Scarlsonj { 3564456Sss150715 int rc; 3574456Sss150715 dlpi_handle_t dh; 3583431Scarlsonj 3594456Sss150715 if (pif->pif_dlpi_hd == NULL) { 3604456Sss150715 if ((rc = dlpi_open(pif->pif_name, &dh, 0)) != DLPI_SUCCESS) { 3614456Sss150715 dhcpmsg(MSG_ERROR, "open_dlpi_pif: dlpi_open: %s", 3624456Sss150715 dlpi_strerror(rc)); 3633431Scarlsonj return (B_FALSE); 3643431Scarlsonj } 3654456Sss150715 3664456Sss150715 if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) { 3674456Sss150715 dhcpmsg(MSG_ERROR, "open_dlpi_pif: dlpi_bind: %s", 3684456Sss150715 dlpi_strerror(rc)); 3694456Sss150715 dlpi_close(dh); 3704456Sss150715 return (B_FALSE); 3714456Sss150715 } 3724456Sss150715 3734456Sss150715 if (!(set_packet_filter(dh, dhcp_filter, NULL, "DHCP"))) { 3744456Sss150715 dlpi_close(dh); 3754456Sss150715 return (B_FALSE); 3764456Sss150715 } 3774456Sss150715 pif->pif_dlpi_id = iu_register_event(eh, dlpi_fd(dh), POLLIN, 3784456Sss150715 dhcp_collect_dlpi, pif); 3794456Sss150715 if (pif->pif_dlpi_id == -1) { 3804456Sss150715 dlpi_close(dh); 3814456Sss150715 return (B_FALSE); 3824456Sss150715 } 3834456Sss150715 3844456Sss150715 pif->pif_dlpi_hd = dh; 3853431Scarlsonj } 3863431Scarlsonj pif->pif_dlpi_count++; 3873431Scarlsonj return (B_TRUE); 3883431Scarlsonj } 3893431Scarlsonj 3903431Scarlsonj /* 3913431Scarlsonj * close_dlpi_pif(): unregister the use of DLPI I/O by a LIF on a PIF, closing 3923431Scarlsonj * the connection if this was the last user. 3933431Scarlsonj * 3943431Scarlsonj * input: dhcp_pif_t *: the physical interface on which we're using DLPI 3953431Scarlsonj * output: none 3963431Scarlsonj */ 3973431Scarlsonj 3983431Scarlsonj void 3993431Scarlsonj close_dlpi_pif(dhcp_pif_t *pif) 4003431Scarlsonj { 4013431Scarlsonj if (pif->pif_dlpi_count > 1) { 4023431Scarlsonj pif->pif_dlpi_count--; 4033431Scarlsonj return; 4043431Scarlsonj } 4053431Scarlsonj pif->pif_dlpi_count = 0; 4063431Scarlsonj if (pif->pif_dlpi_id != -1) { 4073431Scarlsonj (void) iu_unregister_event(eh, pif->pif_dlpi_id, NULL); 4083431Scarlsonj pif->pif_dlpi_id = -1; 4093431Scarlsonj } 4104456Sss150715 if (pif->pif_dlpi_hd != NULL) { 4114456Sss150715 dlpi_close(pif->pif_dlpi_hd); 4124456Sss150715 pif->pif_dlpi_hd = NULL; 4133431Scarlsonj } 4143431Scarlsonj } 4153431Scarlsonj 4163431Scarlsonj /* 4173431Scarlsonj * pif_status(): update the physical interface up/down status. 4183431Scarlsonj * 4193431Scarlsonj * input: dhcp_pif_t *: the physical interface on which we're using DLPI 4203431Scarlsonj * boolean_t: B_TRUE if the interface is going up 4213431Scarlsonj * output: none 4223431Scarlsonj */ 4233431Scarlsonj 4243431Scarlsonj void 4253431Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup) 4263431Scarlsonj { 4273431Scarlsonj dhcp_lif_t *lif; 4283431Scarlsonj dhcp_smach_t *dsmp; 4293431Scarlsonj 4303431Scarlsonj pif->pif_running = isup; 4314516Scarlsonj dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name, 4323431Scarlsonj isup ? "come back up" : "gone down"); 4333431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 4343431Scarlsonj for (dsmp = lif->lif_smachs; dsmp != NULL; 4353431Scarlsonj dsmp = dsmp->dsm_next) { 4363431Scarlsonj if (isup) 4373431Scarlsonj refresh_smach(dsmp); 4383431Scarlsonj else 4393431Scarlsonj remove_default_routes(dsmp); 4403431Scarlsonj } 4413431Scarlsonj } 4423431Scarlsonj } 4433431Scarlsonj 4443431Scarlsonj /* Helper for insert_lif: extract addresses as defined */ 4453431Scarlsonj #define ASSIGN_ADDR(v4, v6, lf) \ 4463431Scarlsonj if (pif->pif_isv6) { \ 4473431Scarlsonj lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \ 4483431Scarlsonj } else { \ 4493431Scarlsonj lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \ 4503431Scarlsonj } 4513431Scarlsonj 4523431Scarlsonj /* 4533431Scarlsonj * insert_lif(): Creates a new logical interface structure and chains it on 4543431Scarlsonj * the list for a given physical interface. Initializes state 4553431Scarlsonj * that remains consistent across all use of the logical 4563431Scarlsonj * interface entry. Caller's PIF hold is transferred to the 4573431Scarlsonj * LIF on success, and is dropped on failure. 4583431Scarlsonj * 4593431Scarlsonj * input: dhcp_pif_t *: pointer to the physical interface for this LIF 4603431Scarlsonj * const char *: the name of the logical interface 4613431Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 4623431Scarlsonj * error code with the reason why 4633431Scarlsonj * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure 4643431Scarlsonj */ 4653431Scarlsonj 4663431Scarlsonj dhcp_lif_t * 4673431Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error) 4683431Scarlsonj { 4693431Scarlsonj dhcp_lif_t *lif; 4703431Scarlsonj int fd; 4713431Scarlsonj struct lifreq lifr; 4723431Scarlsonj 4733431Scarlsonj if ((lif = calloc(1, sizeof (*lif))) == NULL) { 4743431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for " 4753431Scarlsonj "%s", lname); 4760Sstevel@tonic-gate *error = DHCP_IPC_E_MEMORY; 4770Sstevel@tonic-gate return (NULL); 4780Sstevel@tonic-gate } 4790Sstevel@tonic-gate 4803431Scarlsonj lif->lif_sock_ip_fd = -1; 4813431Scarlsonj lif->lif_acknak_id = -1; 4823431Scarlsonj lif->lif_iaid_id = -1; 4833431Scarlsonj lif->lif_hold_count = 1; 4843431Scarlsonj lif->lif_pif = pif; 4853431Scarlsonj lif->lif_removed = B_TRUE; 4863431Scarlsonj init_timer(&lif->lif_preferred, 0); 4873431Scarlsonj init_timer(&lif->lif_expire, 0); 4880Sstevel@tonic-gate 4893431Scarlsonj if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) { 4903431Scarlsonj dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long", 4913431Scarlsonj lname); 4920Sstevel@tonic-gate *error = DHCP_IPC_E_INVIF; 4930Sstevel@tonic-gate goto failure; 4940Sstevel@tonic-gate } 4950Sstevel@tonic-gate 4963431Scarlsonj (void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ); 4973431Scarlsonj 4983431Scarlsonj fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 4990Sstevel@tonic-gate 5003431Scarlsonj if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) 5013431Scarlsonj lif->lif_max = 1024; 5023431Scarlsonj else 5033431Scarlsonj lif->lif_max = lifr.lifr_mtu; 5040Sstevel@tonic-gate 5053431Scarlsonj if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) { 5063431Scarlsonj if (errno == ENXIO) 5073431Scarlsonj *error = DHCP_IPC_E_INVIF; 5083431Scarlsonj else 5093431Scarlsonj *error = DHCP_IPC_E_INT; 5103431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname); 5110Sstevel@tonic-gate goto failure; 5120Sstevel@tonic-gate } 5133431Scarlsonj ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr); 5140Sstevel@tonic-gate 5153431Scarlsonj if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 5162546Scarlsonj if (errno == ENXIO) 5172546Scarlsonj *error = DHCP_IPC_E_INVIF; 5182546Scarlsonj else 5192546Scarlsonj *error = DHCP_IPC_E_INT; 5203431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname); 5213431Scarlsonj goto failure; 5223431Scarlsonj } 5233431Scarlsonj ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr); 5243431Scarlsonj 5253431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 5263431Scarlsonj *error = DHCP_IPC_E_INT; 5273431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname); 5282546Scarlsonj goto failure; 5292546Scarlsonj } 5303431Scarlsonj lif->lif_flags = lifr.lifr_flags; 5313431Scarlsonj 5323431Scarlsonj /* 5333431Scarlsonj * If we've just detected the interface going up or down, then signal 5343431Scarlsonj * an appropriate action. There may be other state machines here. 5353431Scarlsonj */ 5363431Scarlsonj if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) { 5373431Scarlsonj pif_status(pif, B_TRUE); 5383431Scarlsonj } else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) { 5393431Scarlsonj pif_status(pif, B_FALSE); 5403431Scarlsonj } 5413431Scarlsonj 5423431Scarlsonj if (lifr.lifr_flags & IFF_POINTOPOINT) { 5433431Scarlsonj if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) { 5443431Scarlsonj *error = DHCP_IPC_E_INT; 5453431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s", 5463431Scarlsonj lname); 5473431Scarlsonj goto failure; 5483431Scarlsonj } 5493431Scarlsonj ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr); 5503431Scarlsonj } else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) { 5513431Scarlsonj if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) { 5523431Scarlsonj *error = DHCP_IPC_E_INT; 5533431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s", 5543431Scarlsonj lname); 5553431Scarlsonj goto failure; 5563431Scarlsonj } 5573431Scarlsonj lif->lif_broadcast = 5583431Scarlsonj ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr. 5593431Scarlsonj s_addr; 5603431Scarlsonj } 5613431Scarlsonj 5623431Scarlsonj if (pif->pif_isv6) 5633431Scarlsonj cached_v6_max_mtu = 0; 5643431Scarlsonj else 5653431Scarlsonj cached_v4_max_mtu = 0; 5663431Scarlsonj 5673431Scarlsonj lif->lif_removed = B_FALSE; 5683431Scarlsonj insque(lif, &pif->pif_lifs); 5693431Scarlsonj 5703431Scarlsonj return (lif); 5713431Scarlsonj 5723431Scarlsonj failure: 5733431Scarlsonj release_lif(lif); 5743431Scarlsonj return (NULL); 5753431Scarlsonj } 5763431Scarlsonj 5773431Scarlsonj /* 5783431Scarlsonj * hold_lif(): acquire a hold on a logical interface structure. 5793431Scarlsonj * 5803431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 5813431Scarlsonj * output: none 5823431Scarlsonj */ 5833431Scarlsonj 5843431Scarlsonj void 5853431Scarlsonj hold_lif(dhcp_lif_t *lif) 5863431Scarlsonj { 5873431Scarlsonj lif->lif_hold_count++; 5883431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name, 5893431Scarlsonj lif->lif_hold_count); 5903431Scarlsonj } 5913431Scarlsonj 5923431Scarlsonj /* 5933431Scarlsonj * release_lif(): release a hold on a logical interface structure; will 5943431Scarlsonj * destroy the structure on the last hold removed. 5953431Scarlsonj * 5963431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 5973431Scarlsonj * output: none 5983431Scarlsonj */ 5993431Scarlsonj 6003431Scarlsonj void 6013431Scarlsonj release_lif(dhcp_lif_t *lif) 6023431Scarlsonj { 6033431Scarlsonj if (lif->lif_hold_count == 0) { 6043431Scarlsonj dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s", 6053431Scarlsonj lif->lif_name); 6063431Scarlsonj return; 6073431Scarlsonj } 6083431Scarlsonj 6093431Scarlsonj if (lif->lif_hold_count == 1 && !lif->lif_removed) { 6103431Scarlsonj unplumb_lif(lif); 6113431Scarlsonj return; 6123431Scarlsonj } 6133431Scarlsonj 6143431Scarlsonj if (--lif->lif_hold_count == 0) { 6153431Scarlsonj dhcp_pif_t *pif; 6163431Scarlsonj 6173431Scarlsonj dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s", 6183431Scarlsonj lif->lif_name); 6193431Scarlsonj 6203431Scarlsonj if (lif->lif_lease != NULL) 6213431Scarlsonj dhcpmsg(MSG_CRIT, 6223431Scarlsonj "release_lif: still holding lease at last hold!"); 6233431Scarlsonj close_ip_lif(lif); 6243431Scarlsonj pif = lif->lif_pif; 6253431Scarlsonj if (pif->pif_isv6) 6263431Scarlsonj cached_v6_max_mtu = 0; 6273431Scarlsonj else 6283431Scarlsonj cached_v4_max_mtu = 0; 6293431Scarlsonj release_pif(pif); 6303431Scarlsonj free(lif); 6313431Scarlsonj } else { 6323431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u", 6333431Scarlsonj lif->lif_name, lif->lif_hold_count); 6343431Scarlsonj } 6353431Scarlsonj } 6363431Scarlsonj 6373431Scarlsonj /* 6383431Scarlsonj * remove_lif(): remove a logical interface from its PIF and lease (if any) and 6393431Scarlsonj * the lease's hold on the LIF. Assumes that we did not plumb 6403431Scarlsonj * the interface. 6413431Scarlsonj * 6423431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure 6433431Scarlsonj * output: none 6443431Scarlsonj */ 6453431Scarlsonj 6463431Scarlsonj void 6473431Scarlsonj remove_lif(dhcp_lif_t *lif) 6483431Scarlsonj { 6493431Scarlsonj if (lif->lif_plumbed) { 6503431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s", 6513431Scarlsonj lif->lif_name); 6523431Scarlsonj return; 6533431Scarlsonj } 6543431Scarlsonj if (lif->lif_removed) { 6553431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s", 6563431Scarlsonj lif->lif_name); 6573431Scarlsonj } else { 6583431Scarlsonj dhcp_lif_t *lifnext; 6593431Scarlsonj dhcp_lease_t *dlp; 6603431Scarlsonj 6613431Scarlsonj dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name); 6623431Scarlsonj lif->lif_removed = B_TRUE; 6633431Scarlsonj lifnext = lif->lif_next; 6643431Scarlsonj clear_lif_dhcp(lif); 6653431Scarlsonj cancel_lif_timers(lif); 6663431Scarlsonj if (lif->lif_iaid_id != -1 && 6673431Scarlsonj iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) { 6683431Scarlsonj lif->lif_iaid_id = -1; 6693431Scarlsonj release_lif(lif); 6703431Scarlsonj } 6713431Scarlsonj 6723431Scarlsonj /* Remove from PIF list */ 6733431Scarlsonj remque(lif); 6743431Scarlsonj 6753431Scarlsonj /* If we were part of a lease, then remove ourselves */ 6763431Scarlsonj if ((dlp = lif->lif_lease) != NULL) { 6773431Scarlsonj if (--dlp->dl_nlifs == 0) 6783431Scarlsonj dlp->dl_lifs = NULL; 6793431Scarlsonj else if (dlp->dl_lifs == lif) 6803431Scarlsonj dlp->dl_lifs = lifnext; 6813431Scarlsonj if (lif->lif_flags & IFF_DHCPRUNNING) 6823431Scarlsonj clear_lif_dhcp(lif); 6833431Scarlsonj if (lif->lif_declined != NULL) { 6843431Scarlsonj dlp->dl_smach->dsm_lif_down--; 6853431Scarlsonj lif->lif_declined = NULL; 6863431Scarlsonj } 6873431Scarlsonj lif->lif_lease = NULL; 6883431Scarlsonj release_lif(lif); 6893431Scarlsonj } 6903431Scarlsonj } 6913431Scarlsonj } 6923431Scarlsonj 6933431Scarlsonj /* 6943431Scarlsonj * lookup_lif_by_name(): Looks up a logical interface entry given a name and 6953431Scarlsonj * a physical interface. 6963431Scarlsonj * 6973431Scarlsonj * input: const char *: the logical interface name 6983431Scarlsonj * const dhcp_pif_t *: the physical interface 6993431Scarlsonj * output: dhcp_lif_t *: the matching LIF, or NULL if not found 7003431Scarlsonj */ 7013431Scarlsonj 7023431Scarlsonj dhcp_lif_t * 7033431Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif) 7043431Scarlsonj { 7053431Scarlsonj dhcp_lif_t *lif; 7063431Scarlsonj 7073431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 7083431Scarlsonj if (strcmp(lif->lif_name, lname) == 0) 7093431Scarlsonj break; 7103431Scarlsonj } 7113431Scarlsonj 7123431Scarlsonj return (lif); 7133431Scarlsonj } 7143431Scarlsonj 7153431Scarlsonj /* 7163431Scarlsonj * checkaddr(): checks if the given address is still set on the given LIF 7173431Scarlsonj * 7183431Scarlsonj * input: const dhcp_lif_t *: the LIF to check 7193431Scarlsonj * int: the address to look up on the interface (ioctl) 7203431Scarlsonj * const in6_addr_t *: the address to compare to 7213431Scarlsonj * const char *: name of the address for logging purposes 7223431Scarlsonj * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not 7233431Scarlsonj */ 7243431Scarlsonj 7253431Scarlsonj static boolean_t 7263431Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr, 7273431Scarlsonj const char *aname) 7283431Scarlsonj { 7293431Scarlsonj boolean_t isv6; 7303431Scarlsonj int fd; 7313431Scarlsonj struct lifreq lifr; 7323431Scarlsonj 7333431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 7343431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 7353431Scarlsonj 7363431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 7373431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 7383431Scarlsonj 7393431Scarlsonj if (ioctl(fd, ioccmd, &lifr) == -1) { 7403431Scarlsonj if (errno == ENXIO) { 7413431Scarlsonj dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone", 7423431Scarlsonj lif->lif_name); 7433431Scarlsonj return (B_FALSE); 7443431Scarlsonj } 7453431Scarlsonj dhcpmsg(MSG_DEBUG, 7463431Scarlsonj "checkaddr: ignoring ioctl error on %s %x: %s", 7473431Scarlsonj lif->lif_name, ioccmd, strerror(errno)); 7483431Scarlsonj } else if (isv6) { 7493431Scarlsonj struct sockaddr_in6 *sin6 = 7503431Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr; 7513431Scarlsonj char abuf1[INET6_ADDRSTRLEN]; 7523431Scarlsonj char abuf2[INET6_ADDRSTRLEN]; 7533431Scarlsonj 7543431Scarlsonj if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) { 7553431Scarlsonj dhcpmsg(MSG_WARNING, 7563431Scarlsonj "checkaddr: expected %s %s on %s, have %s", 7573431Scarlsonj aname, inet_ntop(AF_INET6, &sin6->sin6_addr, abuf1, 7583431Scarlsonj sizeof (abuf1)), lif->lif_name, 7593431Scarlsonj inet_ntop(AF_INET6, addr, abuf2, sizeof (abuf2))); 7603431Scarlsonj return (B_FALSE); 7613431Scarlsonj } 7623431Scarlsonj } else { 7633431Scarlsonj struct sockaddr_in *sinp = 7643431Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr; 7653431Scarlsonj ipaddr_t v4addr; 7663431Scarlsonj char abuf1[INET_ADDRSTRLEN]; 7673431Scarlsonj char abuf2[INET_ADDRSTRLEN]; 7683431Scarlsonj 7693431Scarlsonj IN6_V4MAPPED_TO_IPADDR(addr, v4addr); 7703431Scarlsonj if (sinp->sin_addr.s_addr != v4addr) { 7713431Scarlsonj dhcpmsg(MSG_WARNING, 7723431Scarlsonj "checkaddr: expected %s %s on %s, have %s", 7733431Scarlsonj aname, inet_ntop(AF_INET, &sinp->sin_addr, abuf1, 7743431Scarlsonj sizeof (abuf1)), lif->lif_name, 7753431Scarlsonj inet_ntop(AF_INET, &v4addr, abuf2, 7763431Scarlsonj sizeof (abuf2))); 7773431Scarlsonj return (B_FALSE); 7783431Scarlsonj } 7793431Scarlsonj } 7803431Scarlsonj return (B_TRUE); 7813431Scarlsonj } 7823431Scarlsonj 7833431Scarlsonj /* 7843431Scarlsonj * verify_lif(): verifies than a LIF is still valid (i.e., has not been 7853431Scarlsonj * explicitly or implicitly dropped or released) 7863431Scarlsonj * 7873431Scarlsonj * input: const dhcp_lif_t *: the LIF to verify 7883431Scarlsonj * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise 7893431Scarlsonj */ 7903431Scarlsonj 7913431Scarlsonj boolean_t 7923431Scarlsonj verify_lif(const dhcp_lif_t *lif) 7933431Scarlsonj { 7943431Scarlsonj boolean_t isv6; 7953431Scarlsonj int fd; 7963431Scarlsonj struct lifreq lifr; 7973431Scarlsonj 7983431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 7993431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 8003431Scarlsonj 8013431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 8023431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 8033431Scarlsonj 8043431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 8054106Scarlsonj if (errno != ENXIO) { 8064106Scarlsonj dhcpmsg(MSG_ERR, 8074106Scarlsonj "verify_lif: SIOCGLIFFLAGS failed on %s", 8084106Scarlsonj lif->lif_name); 8094106Scarlsonj } 8103431Scarlsonj return (B_FALSE); 8113431Scarlsonj } 8123431Scarlsonj 8133431Scarlsonj /* 8143431Scarlsonj * If important flags have changed, then abandon the interface. 8153431Scarlsonj */ 8163431Scarlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) { 8173431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: " 8183431Scarlsonj "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags, 8193431Scarlsonj lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) & 8203431Scarlsonj DHCP_IFF_WATCH); 8213431Scarlsonj return (B_FALSE); 8223431Scarlsonj } 8233431Scarlsonj 8243431Scarlsonj /* 8253431Scarlsonj * Special case: if the interface has gone down as a duplicate, then 8263431Scarlsonj * this alone does _not_ mean that we're abandoning it just yet. Allow 8273431Scarlsonj * the state machine to handle this normally by trying to get a new 8283431Scarlsonj * lease. 8293431Scarlsonj */ 8303431Scarlsonj if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) { 8313431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s", 8323431Scarlsonj lif->lif_name); 8333431Scarlsonj return (B_TRUE); 8343431Scarlsonj } 8353431Scarlsonj 8363431Scarlsonj /* 8373431Scarlsonj * If the user has torn down or started up the interface manually, then 8383431Scarlsonj * abandon the lease. 8393431Scarlsonj */ 8403431Scarlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) { 8413431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s", 8423431Scarlsonj lifr.lifr_flags & IFF_UP ? "started up" : "shut down", 8433431Scarlsonj lif->lif_name); 8443431Scarlsonj return (B_FALSE); 8453431Scarlsonj } 8463431Scarlsonj 8473431Scarlsonj /* 8483431Scarlsonj * Check for delete and recreate. 8493431Scarlsonj */ 8503431Scarlsonj if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 8513431Scarlsonj dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s", 8523431Scarlsonj lif->lif_name); 8533431Scarlsonj return (B_FALSE); 8543431Scarlsonj } 8553431Scarlsonj if (lifr.lifr_index != lif->lif_pif->pif_index) { 8563431Scarlsonj dhcpmsg(MSG_DEBUG, 8573431Scarlsonj "verify_lif: ifindex on %s changed: %u to %u", 8583431Scarlsonj lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index); 8593431Scarlsonj return (B_FALSE); 8603431Scarlsonj } 8613431Scarlsonj 8623431Scarlsonj /* 8633431Scarlsonj * If the IP address, netmask, or broadcast address have changed, or 8643431Scarlsonj * the interface has been unplumbed, then we act like there has been an 8653431Scarlsonj * implicit drop. (Note that the netmask is under DHCP control for 8663431Scarlsonj * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast 8673431Scarlsonj * addresses.) 8683431Scarlsonj */ 8692546Scarlsonj 8703431Scarlsonj if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address")) 8713431Scarlsonj return (B_FALSE); 8723431Scarlsonj 8733431Scarlsonj if (isv6) { 8743431Scarlsonj /* 8753431Scarlsonj * If it's not point-to-point, we're done. If it is, then 8763431Scarlsonj * check the peer's address as well. 8773431Scarlsonj */ 8783431Scarlsonj return (!(lif->lif_flags & IFF_POINTOPOINT) || 8793431Scarlsonj checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer, 8803431Scarlsonj "peer address")); 8813431Scarlsonj } else { 8823431Scarlsonj if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask, 8833431Scarlsonj "netmask")) 8843431Scarlsonj return (B_FALSE); 8853431Scarlsonj 8863431Scarlsonj return (checkaddr(lif, 8873431Scarlsonj (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR : 8883431Scarlsonj SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address")); 8893431Scarlsonj } 8903431Scarlsonj } 8913431Scarlsonj 8923431Scarlsonj /* 8933431Scarlsonj * canonize_lif(): puts the interface in a canonical (zeroed) form. This is 8943431Scarlsonj * used only on the "main" LIF for IPv4. All other interfaces 8953431Scarlsonj * are under dhcpagent control and are removed using 8963431Scarlsonj * unplumb_lif(). 8973431Scarlsonj * 8983431Scarlsonj * input: dhcp_lif_t *: the interface to canonize 8993431Scarlsonj * output: none 9003431Scarlsonj */ 9013431Scarlsonj 9023431Scarlsonj static void 9033431Scarlsonj canonize_lif(dhcp_lif_t *lif) 9043431Scarlsonj { 9053431Scarlsonj boolean_t isv6; 9063431Scarlsonj int fd; 9073431Scarlsonj struct lifreq lifr; 9083431Scarlsonj 9093431Scarlsonj /* 9103431Scarlsonj * If there's nothing here, then don't touch the interface. This can 9113431Scarlsonj * happen when an already-canonized LIF is recanonized. 9123431Scarlsonj */ 9133431Scarlsonj if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr)) 9143431Scarlsonj return; 9153431Scarlsonj 9163431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 9173431Scarlsonj dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s", 9183431Scarlsonj isv6 ? 6 : 4, lif->lif_name); 9193431Scarlsonj 9203431Scarlsonj lif->lif_v6addr = my_in6addr_any; 9213431Scarlsonj lif->lif_v6mask = my_in6addr_any; 9223431Scarlsonj lif->lif_v6peer = my_in6addr_any; 9233431Scarlsonj 9243431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 9253431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 9263431Scarlsonj 9273431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd; 9283431Scarlsonj 9293431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 9304106Scarlsonj if (errno != ENXIO) { 9314106Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s", 9324106Scarlsonj lif->lif_name); 9334106Scarlsonj } 9343431Scarlsonj return; 9353431Scarlsonj } 9363431Scarlsonj 9373431Scarlsonj /* Should not happen */ 9383431Scarlsonj if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) { 9393431Scarlsonj dhcpmsg(MSG_INFO, 9403431Scarlsonj "canonize_lif: cannot clear %s; flags are %llx", 9413431Scarlsonj lif->lif_name, lifr.lifr_flags); 9423431Scarlsonj return; 9433431Scarlsonj } 9443431Scarlsonj 9453431Scarlsonj /* 9463431Scarlsonj * clear the UP flag, but don't clear DHCPRUNNING since 9473431Scarlsonj * that should only be done when the interface is removed 9483431Scarlsonj * (see clear_lif_dhcp() and remove_lif()) 9493431Scarlsonj */ 9503431Scarlsonj 9513431Scarlsonj lif->lif_flags = lifr.lifr_flags &= ~IFF_UP; 9523431Scarlsonj 9533431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 9543431Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't set flags for %s", 9553431Scarlsonj lif->lif_name); 9563431Scarlsonj return; 9573431Scarlsonj } 9583431Scarlsonj 9593431Scarlsonj (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); 9603431Scarlsonj if (isv6) { 9613431Scarlsonj struct sockaddr_in6 *sin6 = 9623431Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr; 9633431Scarlsonj 9643431Scarlsonj sin6->sin6_family = AF_INET6; 9653431Scarlsonj sin6->sin6_addr = my_in6addr_any; 9663431Scarlsonj } else { 9673431Scarlsonj struct sockaddr_in *sinv = 9683431Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr; 9693431Scarlsonj 9703431Scarlsonj sinv->sin_family = AF_INET; 9713431Scarlsonj sinv->sin_addr.s_addr = htonl(INADDR_ANY); 9723431Scarlsonj } 9733431Scarlsonj 9743431Scarlsonj if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) { 9753431Scarlsonj dhcpmsg(MSG_ERR, 9763431Scarlsonj "canonize_lif: can't clear local address on %s", 9773431Scarlsonj lif->lif_name); 9783431Scarlsonj } 9793431Scarlsonj 9803431Scarlsonj /* Netmask is under in.ndpd control with IPv6 */ 9813431Scarlsonj if (!isv6 && ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) { 9823431Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't clear netmask on %s", 9833431Scarlsonj lif->lif_name); 9843431Scarlsonj } 9853431Scarlsonj 9863431Scarlsonj if (lif->lif_flags & IFF_POINTOPOINT) { 9873431Scarlsonj if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) { 9883431Scarlsonj dhcpmsg(MSG_ERR, 9893431Scarlsonj "canonize_lif: can't clear remote address on %s", 9903431Scarlsonj lif->lif_name); 9913431Scarlsonj } 9923431Scarlsonj } else if (!isv6) { 9933431Scarlsonj if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) { 9943431Scarlsonj dhcpmsg(MSG_ERR, 9953431Scarlsonj "canonize_lif: can't clear broadcast address on %s", 9963431Scarlsonj lif->lif_name); 9973431Scarlsonj } 9983431Scarlsonj } 9993431Scarlsonj } 10003431Scarlsonj 10013431Scarlsonj /* 10023431Scarlsonj * plumb_lif(): Adds the LIF to the system. This is used for all 10033431Scarlsonj * DHCPv6-derived interfaces. The returned LIF has a hold 10043431Scarlsonj * on it. 10053431Scarlsonj * 10063431Scarlsonj * input: dhcp_lif_t *: the interface to unplumb 10073431Scarlsonj * output: none 10083431Scarlsonj */ 10093431Scarlsonj 10103431Scarlsonj dhcp_lif_t * 10113431Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr) 10123431Scarlsonj { 10133431Scarlsonj dhcp_lif_t *lif; 10143431Scarlsonj char abuf[INET6_ADDRSTRLEN]; 10153431Scarlsonj struct lifreq lifr; 10163431Scarlsonj struct sockaddr_in6 *sin6; 10173431Scarlsonj int error; 10183431Scarlsonj 10193431Scarlsonj (void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)); 10203431Scarlsonj 10213431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 10223431Scarlsonj if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) { 10233431Scarlsonj dhcpmsg(MSG_ERR, 10243431Scarlsonj "plumb_lif: entry for %s already exists!", abuf); 10253431Scarlsonj return (NULL); 10263431Scarlsonj } 10273431Scarlsonj } 10283431Scarlsonj 10293431Scarlsonj /* First, create a new zero-address logical interface */ 10303431Scarlsonj (void) memset(&lifr, 0, sizeof (lifr)); 10313431Scarlsonj (void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name)); 10323431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) { 10333431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name); 10343431Scarlsonj return (NULL); 10353431Scarlsonj } 10363431Scarlsonj 10373431Scarlsonj /* Next, set the netmask to all ones */ 10383431Scarlsonj sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 10393431Scarlsonj sin6->sin6_family = AF_INET6; 10403431Scarlsonj (void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr)); 10413431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 10423431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s", 10433431Scarlsonj lifr.lifr_name); 10440Sstevel@tonic-gate goto failure; 10450Sstevel@tonic-gate } 10460Sstevel@tonic-gate 10473431Scarlsonj /* Now set the interface address */ 10483431Scarlsonj sin6->sin6_addr = *addr; 10493431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 10503431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s", 10513431Scarlsonj lifr.lifr_name, abuf); 10523431Scarlsonj goto failure; 10533431Scarlsonj } 10543431Scarlsonj 10553431Scarlsonj /* Mark the interface up */ 10563431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 10573431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s", 10583431Scarlsonj lifr.lifr_name); 10593431Scarlsonj goto failure; 10603431Scarlsonj } 10613431Scarlsonj lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING; 10623431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 10633431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s", 10643431Scarlsonj lifr.lifr_name); 10653431Scarlsonj goto failure; 10663431Scarlsonj } 10673431Scarlsonj 10683431Scarlsonj /* Now we can create the internal LIF structure */ 10693431Scarlsonj hold_pif(pif); 10703431Scarlsonj if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL) 10713431Scarlsonj goto failure; 10723431Scarlsonj 10733431Scarlsonj dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf, 10743431Scarlsonj lif->lif_name); 10753431Scarlsonj lif->lif_plumbed = B_TRUE; 10763431Scarlsonj 10773431Scarlsonj return (lif); 10783431Scarlsonj 10793431Scarlsonj failure: 10803431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 10813431Scarlsonj errno != ENXIO) { 10823431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s", 10833431Scarlsonj lifr.lifr_name); 10843431Scarlsonj } 10853431Scarlsonj return (NULL); 10863431Scarlsonj } 10873431Scarlsonj 10883431Scarlsonj /* 10893431Scarlsonj * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used 10903431Scarlsonj * for all interfaces configured by DHCP (those in leases). 10913431Scarlsonj * 10923431Scarlsonj * input: dhcp_lif_t *: the interface to unplumb 10933431Scarlsonj * output: none 10943431Scarlsonj */ 10953431Scarlsonj 10963431Scarlsonj void 10973431Scarlsonj unplumb_lif(dhcp_lif_t *lif) 10983431Scarlsonj { 10993431Scarlsonj dhcp_lease_t *dlp; 11003431Scarlsonj 11013431Scarlsonj if (lif->lif_plumbed) { 11023431Scarlsonj struct lifreq lifr; 11033431Scarlsonj 11043431Scarlsonj (void) memset(&lifr, 0, sizeof (lifr)); 11053431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, 11063431Scarlsonj sizeof (lifr.lifr_name)); 11073431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 11083431Scarlsonj errno != ENXIO) { 11093431Scarlsonj dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s", 11103431Scarlsonj lif->lif_name); 11113431Scarlsonj } 11123431Scarlsonj lif->lif_plumbed = B_FALSE; 11133431Scarlsonj } 11143431Scarlsonj lif->lif_flags = 0; 11153431Scarlsonj /* 11163431Scarlsonj * Special case: if we're "unplumbing" the main LIF for DHCPv4, then 11173431Scarlsonj * just canonize it and remove it from the lease. 11183431Scarlsonj */ 11193431Scarlsonj if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) { 11203431Scarlsonj canonize_lif(lif); 11213431Scarlsonj cancel_lif_timers(lif); 11223431Scarlsonj if (lif->lif_declined != NULL) { 11233431Scarlsonj dlp->dl_smach->dsm_lif_down--; 11243431Scarlsonj lif->lif_declined = NULL; 11253431Scarlsonj } 11263431Scarlsonj dlp->dl_nlifs = 0; 11273431Scarlsonj dlp->dl_lifs = NULL; 11283431Scarlsonj lif->lif_lease = NULL; 11293431Scarlsonj release_lif(lif); 11303431Scarlsonj } else { 11313431Scarlsonj remove_lif(lif); 11323431Scarlsonj } 11333431Scarlsonj } 11343431Scarlsonj 11353431Scarlsonj /* 11363431Scarlsonj * attach_lif(): create a new logical interface, creating the physical 11373431Scarlsonj * interface as necessary. 11383431Scarlsonj * 11393431Scarlsonj * input: const char *: the logical interface name 11403431Scarlsonj * boolean_t: B_TRUE for IPv6 11413431Scarlsonj * int *: set to DHCP_IPC_E_* if creation fails 11423431Scarlsonj * output: dhcp_lif_t *: pointer to new entry, or NULL on failure 11433431Scarlsonj */ 11443431Scarlsonj 11453431Scarlsonj dhcp_lif_t * 11463431Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error) 11473431Scarlsonj { 11483431Scarlsonj dhcp_pif_t *pif; 11493431Scarlsonj char pname[LIFNAMSIZ], *cp; 11503431Scarlsonj 11513431Scarlsonj (void) strlcpy(pname, lname, sizeof (pname)); 11523431Scarlsonj if ((cp = strchr(pname, ':')) != NULL) 11533431Scarlsonj *cp = '\0'; 11543431Scarlsonj 11553431Scarlsonj if ((pif = lookup_pif_by_name(pname, isv6)) != NULL) 11563431Scarlsonj hold_pif(pif); 11573431Scarlsonj else if ((pif = insert_pif(pname, isv6, error)) == NULL) 11583431Scarlsonj return (NULL); 11593431Scarlsonj 11603431Scarlsonj if (lookup_lif_by_name(lname, pif) != NULL) { 11613431Scarlsonj dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!", 11623431Scarlsonj lname); 11633431Scarlsonj release_pif(pif); 11643431Scarlsonj *error = DHCP_IPC_E_INVIF; 11653431Scarlsonj return (NULL); 11663431Scarlsonj } 11673431Scarlsonj 11683431Scarlsonj /* If LIF creation fails, then insert_lif discards our PIF hold */ 11693431Scarlsonj return (insert_lif(pif, lname, error)); 11703431Scarlsonj } 11713431Scarlsonj 11723431Scarlsonj /* 11733431Scarlsonj * set_lif_dhcp(): Set logical interface flags to show that it's managed 11743431Scarlsonj * by DHCP. 11753431Scarlsonj * 11763431Scarlsonj * input: dhcp_lif_t *: the logical interface 11773431Scarlsonj * boolean_t: B_TRUE if adopting 11783431Scarlsonj * output: int: set to DHCP_IPC_E_* if operation fails 11793431Scarlsonj */ 11803431Scarlsonj 11813431Scarlsonj int 11823431Scarlsonj set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting) 11833431Scarlsonj { 11843431Scarlsonj int fd; 11853431Scarlsonj int err; 11863431Scarlsonj struct lifreq lifr; 11873431Scarlsonj 11883431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 11893431Scarlsonj 11903431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 11913431Scarlsonj 11923431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 11933431Scarlsonj err = errno; 11943431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s", 11953431Scarlsonj lif->lif_name); 11963431Scarlsonj return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT); 11973431Scarlsonj } 11983431Scarlsonj lif->lif_flags = lifr.lifr_flags; 11993431Scarlsonj 12003431Scarlsonj /* 12013431Scarlsonj * Check for conflicting sources of address control, and other 12023431Scarlsonj * unacceptable configurations. 12033431Scarlsonj */ 1204*4823Sseb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 1205*4823Sseb IFF_VIRTUAL)) { 12063431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx", 12073431Scarlsonj lif->lif_name, lifr.lifr_flags); 12083431Scarlsonj return (DHCP_IPC_E_INVIF); 12093431Scarlsonj } 12103431Scarlsonj 12110Sstevel@tonic-gate /* 12120Sstevel@tonic-gate * if DHCPRUNNING is already set on the interface and we're 12130Sstevel@tonic-gate * not adopting it, the agent probably crashed and burned. 12140Sstevel@tonic-gate * note it, but don't let it stop the proceedings. we're 12150Sstevel@tonic-gate * pretty sure we're not already running, since we wouldn't 12160Sstevel@tonic-gate * have been able to bind to our IPC port. 12170Sstevel@tonic-gate */ 12180Sstevel@tonic-gate 12193431Scarlsonj if (lifr.lifr_flags & IFF_DHCPRUNNING) { 12203431Scarlsonj if (!is_adopting) { 12213431Scarlsonj dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already " 12223431Scarlsonj "set on %s", lif->lif_name); 12233431Scarlsonj } 12243431Scarlsonj } else { 12253431Scarlsonj lifr.lifr_flags |= IFF_DHCPRUNNING; 12263431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 12273431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s", 12283431Scarlsonj lif->lif_name); 12293431Scarlsonj return (DHCP_IPC_E_INT); 12303431Scarlsonj } 12313431Scarlsonj lif->lif_flags = lifr.lifr_flags; 12320Sstevel@tonic-gate } 12333431Scarlsonj return (DHCP_IPC_SUCCESS); 12343431Scarlsonj } 12350Sstevel@tonic-gate 12363431Scarlsonj /* 12373431Scarlsonj * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer 12383431Scarlsonj * managed by DHCP. 12393431Scarlsonj * 12403431Scarlsonj * input: dhcp_lif_t *: the logical interface 12413431Scarlsonj * output: none 12423431Scarlsonj */ 12430Sstevel@tonic-gate 12443431Scarlsonj static void 12453431Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif) 12463431Scarlsonj { 12473431Scarlsonj int fd; 12483431Scarlsonj struct lifreq lifr; 12490Sstevel@tonic-gate 12503431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 12513431Scarlsonj 12523431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 12533431Scarlsonj 12543431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 12553431Scarlsonj return; 12560Sstevel@tonic-gate 12573431Scarlsonj if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) 12583431Scarlsonj return; 12590Sstevel@tonic-gate 12603431Scarlsonj lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING; 12613431Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 12623431Scarlsonj } 12630Sstevel@tonic-gate 12643431Scarlsonj /* 12653431Scarlsonj * set_lif_deprecated(): Set the "deprecated" flag to tell users that this 12663431Scarlsonj * address will be going away. As the interface is 12673431Scarlsonj * going away, we don't care if there are errors. 12683431Scarlsonj * 12693431Scarlsonj * input: dhcp_lif_t *: the logical interface 12703431Scarlsonj * output: none 12713431Scarlsonj */ 12720Sstevel@tonic-gate 12733431Scarlsonj void 12743431Scarlsonj set_lif_deprecated(dhcp_lif_t *lif) 12753431Scarlsonj { 12763431Scarlsonj int fd; 12773431Scarlsonj struct lifreq lifr; 12780Sstevel@tonic-gate 12793431Scarlsonj if (lif->lif_flags & IFF_DEPRECATED) 12803431Scarlsonj return; 12810Sstevel@tonic-gate 12823431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 12833431Scarlsonj 12843431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 12850Sstevel@tonic-gate 12863431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 12873431Scarlsonj return; 12883431Scarlsonj 12893431Scarlsonj if (lifr.lifr_flags & IFF_DEPRECATED) 12903431Scarlsonj return; 12910Sstevel@tonic-gate 12923431Scarlsonj lifr.lifr_flags |= IFF_DEPRECATED; 12933431Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 12943431Scarlsonj lif->lif_flags = lifr.lifr_flags; 12953431Scarlsonj } 12963431Scarlsonj 12973431Scarlsonj /* 12983431Scarlsonj * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this 12993431Scarlsonj * address will not be going away. This happens if we 13003431Scarlsonj * get a renewal after preferred lifetime but before 13013431Scarlsonj * the valid lifetime. 13023431Scarlsonj * 13033431Scarlsonj * input: dhcp_lif_t *: the logical interface 13043431Scarlsonj * output: boolean_t: B_TRUE on success. 13053431Scarlsonj */ 13060Sstevel@tonic-gate 13073431Scarlsonj boolean_t 13083431Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif) 13093431Scarlsonj { 13103431Scarlsonj int fd; 13113431Scarlsonj struct lifreq lifr; 13123431Scarlsonj 13133431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 13143431Scarlsonj 13153431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 13163431Scarlsonj 13173431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 13183431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s", 13193431Scarlsonj lif->lif_name); 13203431Scarlsonj return (B_FALSE); 13210Sstevel@tonic-gate } 13220Sstevel@tonic-gate 13230Sstevel@tonic-gate /* 13243431Scarlsonj * Check for conflicting sources of address control, and other 13253431Scarlsonj * unacceptable configurations. 13260Sstevel@tonic-gate */ 1327*4823Sseb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 1328*4823Sseb IFF_VIRTUAL)) { 13293431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags " 13303431Scarlsonj "are %llx", lif->lif_name, lifr.lifr_flags); 13313431Scarlsonj return (B_FALSE); 13320Sstevel@tonic-gate } 13330Sstevel@tonic-gate 13343431Scarlsonj if (!(lifr.lifr_flags & IFF_DEPRECATED)) 13353431Scarlsonj return (B_TRUE); 13360Sstevel@tonic-gate 13373431Scarlsonj lifr.lifr_flags &= ~IFF_DEPRECATED; 13383431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 13393431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s", 13403431Scarlsonj lif->lif_name); 13413431Scarlsonj return (B_FALSE); 13423431Scarlsonj } else { 13433431Scarlsonj lif->lif_flags = lifr.lifr_flags; 13443431Scarlsonj return (B_TRUE); 13450Sstevel@tonic-gate } 13460Sstevel@tonic-gate } 13470Sstevel@tonic-gate 13480Sstevel@tonic-gate /* 13493431Scarlsonj * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only). 13500Sstevel@tonic-gate * 13513431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 13523431Scarlsonj * output: boolean_t: B_TRUE if the socket was opened successfully. 13530Sstevel@tonic-gate */ 13540Sstevel@tonic-gate 13553431Scarlsonj boolean_t 13563431Scarlsonj open_ip_lif(dhcp_lif_t *lif) 13570Sstevel@tonic-gate { 13583431Scarlsonj if (lif->lif_sock_ip_fd != -1) { 13593431Scarlsonj dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s", 13603431Scarlsonj lif->lif_name); 13613431Scarlsonj return (B_FALSE); 13620Sstevel@tonic-gate } 13630Sstevel@tonic-gate 13643431Scarlsonj lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0); 13653431Scarlsonj if (lif->lif_sock_ip_fd == -1) { 13663431Scarlsonj dhcpmsg(MSG_ERR, "open_ip_lif: cannot create v4 socket on %s", 13673431Scarlsonj lif->lif_name); 13683431Scarlsonj return (B_FALSE); 13690Sstevel@tonic-gate } 13700Sstevel@tonic-gate 13713431Scarlsonj if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, 13723431Scarlsonj ntohl(lif->lif_addr))) { 13733431Scarlsonj dhcpmsg(MSG_ERR, "open_ip_lif: cannot bind v4 socket on %s", 13743431Scarlsonj lif->lif_name); 13753431Scarlsonj return (B_FALSE); 13763431Scarlsonj } 13770Sstevel@tonic-gate 13783431Scarlsonj lif->lif_acknak_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN, 13793431Scarlsonj dhcp_acknak_lif, lif); 13803431Scarlsonj if (lif->lif_acknak_id == -1) { 13813431Scarlsonj dhcpmsg(MSG_WARNING, "open_ip_lif: cannot register to " 13823431Scarlsonj "receive IP unicast"); 13833431Scarlsonj close_ip_lif(lif); 13843431Scarlsonj return (B_FALSE); 13850Sstevel@tonic-gate } 13860Sstevel@tonic-gate return (B_TRUE); 13870Sstevel@tonic-gate } 13880Sstevel@tonic-gate 13890Sstevel@tonic-gate /* 13903431Scarlsonj * close_ip_lif(): close an IP socket for I/O on a given LIF. 13910Sstevel@tonic-gate * 13923431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 13933431Scarlsonj * output: none 13940Sstevel@tonic-gate */ 13950Sstevel@tonic-gate 13963431Scarlsonj void 13973431Scarlsonj close_ip_lif(dhcp_lif_t *lif) 13980Sstevel@tonic-gate { 13993431Scarlsonj if (lif->lif_acknak_id != -1) { 14003431Scarlsonj (void) iu_unregister_event(eh, lif->lif_acknak_id, NULL); 14013431Scarlsonj lif->lif_acknak_id = -1; 14023431Scarlsonj } 14033431Scarlsonj if (lif->lif_sock_ip_fd != -1) { 14043431Scarlsonj (void) close(lif->lif_sock_ip_fd); 14053431Scarlsonj lif->lif_sock_ip_fd = -1; 14063431Scarlsonj } 14073431Scarlsonj } 14080Sstevel@tonic-gate 14093431Scarlsonj /* 14103431Scarlsonj * lif_mark_decline(): mark a LIF as having been declined due to a duplicate 14113431Scarlsonj * address or some other conflict. This is used in 14123431Scarlsonj * send_declines() to report failure back to the server. 14133431Scarlsonj * 14143431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 14153431Scarlsonj * const char *: text string explaining why the address is declined 14163431Scarlsonj * output: none 14173431Scarlsonj */ 14180Sstevel@tonic-gate 14193431Scarlsonj void 14203431Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason) 14213431Scarlsonj { 14223431Scarlsonj if (lif->lif_declined == NULL) { 14233431Scarlsonj dhcp_lease_t *dlp; 14240Sstevel@tonic-gate 14253431Scarlsonj lif->lif_declined = reason; 14263431Scarlsonj if ((dlp = lif->lif_lease) != NULL) 14273431Scarlsonj dlp->dl_smach->dsm_lif_down++; 14280Sstevel@tonic-gate } 14290Sstevel@tonic-gate } 14300Sstevel@tonic-gate 14310Sstevel@tonic-gate /* 14323431Scarlsonj * schedule_lif_timer(): schedules the LIF-related timer 14330Sstevel@tonic-gate * 14343431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 14353431Scarlsonj * dhcp_timer_t *: the timer to schedule 14363431Scarlsonj * iu_tq_callback_t *: the callback to call upon firing 14373431Scarlsonj * output: boolean_t: B_TRUE if the timer was scheduled successfully 14380Sstevel@tonic-gate */ 14390Sstevel@tonic-gate 14403431Scarlsonj boolean_t 14413431Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire) 14420Sstevel@tonic-gate { 14430Sstevel@tonic-gate /* 14443431Scarlsonj * If there's a timer running, cancel it and release its lease 14453431Scarlsonj * reference. 14460Sstevel@tonic-gate */ 14473431Scarlsonj if (dt->dt_id != -1) { 14483431Scarlsonj if (!cancel_timer(dt)) 14493431Scarlsonj return (B_FALSE); 14503431Scarlsonj release_lif(lif); 14513431Scarlsonj } 14520Sstevel@tonic-gate 14533431Scarlsonj if (schedule_timer(dt, expire, lif)) { 14543431Scarlsonj hold_lif(lif); 14553431Scarlsonj return (B_TRUE); 14563431Scarlsonj } else { 14573431Scarlsonj dhcpmsg(MSG_WARNING, 14583431Scarlsonj "schedule_lif_timer: cannot schedule timer"); 14593431Scarlsonj return (B_FALSE); 14603431Scarlsonj } 14610Sstevel@tonic-gate } 14620Sstevel@tonic-gate 14630Sstevel@tonic-gate /* 14643431Scarlsonj * cancel_lif_timer(): cancels a LIF-related timer 14650Sstevel@tonic-gate * 14663431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 14673431Scarlsonj * dhcp_timer_t *: the timer to cancel 14683431Scarlsonj * output: none 14690Sstevel@tonic-gate */ 14700Sstevel@tonic-gate 14713431Scarlsonj static void 14723431Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt) 14730Sstevel@tonic-gate { 14743431Scarlsonj if (dt->dt_id == -1) 14753431Scarlsonj return; 14763431Scarlsonj if (cancel_timer(dt)) { 14773431Scarlsonj dhcpmsg(MSG_DEBUG2, 14783431Scarlsonj "cancel_lif_timer: canceled expiry timer on %s", 14793431Scarlsonj lif->lif_name); 14803431Scarlsonj release_lif(lif); 14813431Scarlsonj } else { 14823431Scarlsonj dhcpmsg(MSG_WARNING, 14833431Scarlsonj "cancel_lif_timer: cannot cancel timer on %s", 14843431Scarlsonj lif->lif_name); 14850Sstevel@tonic-gate } 14860Sstevel@tonic-gate } 14870Sstevel@tonic-gate 14880Sstevel@tonic-gate /* 14893431Scarlsonj * cancel_lif_timers(): cancels the LIF-related timers 14900Sstevel@tonic-gate * 14913431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on 14923431Scarlsonj * output: none 14930Sstevel@tonic-gate */ 14940Sstevel@tonic-gate 14950Sstevel@tonic-gate void 14963431Scarlsonj cancel_lif_timers(dhcp_lif_t *lif) 14970Sstevel@tonic-gate { 14983431Scarlsonj cancel_lif_timer(lif, &lif->lif_preferred); 14993431Scarlsonj cancel_lif_timer(lif, &lif->lif_expire); 15000Sstevel@tonic-gate } 15010Sstevel@tonic-gate 15020Sstevel@tonic-gate /* 15033431Scarlsonj * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common 15043431Scarlsonj * file descriptors (v4_sock_fd and v6_sock_fd). 15050Sstevel@tonic-gate * 15063431Scarlsonj * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4 15073431Scarlsonj * output: none 15080Sstevel@tonic-gate */ 15090Sstevel@tonic-gate 15103431Scarlsonj uint_t 15113431Scarlsonj get_max_mtu(boolean_t isv6) 15120Sstevel@tonic-gate { 15133431Scarlsonj uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu; 15143431Scarlsonj 15153431Scarlsonj if (*mtup == 0) { 15163431Scarlsonj dhcp_pif_t *pif; 15173431Scarlsonj dhcp_lif_t *lif; 15183431Scarlsonj struct lifreq lifr; 15193431Scarlsonj 15203431Scarlsonj /* Set an arbitrary lower bound */ 15213431Scarlsonj *mtup = 1024; 15223431Scarlsonj pif = isv6 ? v6root : v4root; 15233431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 15243431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; 15253431Scarlsonj lif = lif->lif_next) { 15263431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, 15273431Scarlsonj LIFNAMSIZ); 15283431Scarlsonj if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) != 15293431Scarlsonj -1 && lifr.lifr_mtu > *mtup) { 15303431Scarlsonj *mtup = lifr.lifr_mtu; 15313431Scarlsonj } 15323431Scarlsonj } 15333431Scarlsonj } 15340Sstevel@tonic-gate } 15353431Scarlsonj return (*mtup); 15360Sstevel@tonic-gate } 15370Sstevel@tonic-gate 15380Sstevel@tonic-gate /* 15393431Scarlsonj * expired_lif_state(): summarize the state of expired LIFs on a given state 15403431Scarlsonj * machine. 15410Sstevel@tonic-gate * 15423431Scarlsonj * input: dhcp_smach_t *: the state machine to scan 15433431Scarlsonj * output: dhcp_expire_t: overall state 15440Sstevel@tonic-gate */ 15450Sstevel@tonic-gate 15463431Scarlsonj dhcp_expire_t 15473431Scarlsonj expired_lif_state(dhcp_smach_t *dsmp) 15480Sstevel@tonic-gate { 15493431Scarlsonj dhcp_lease_t *dlp; 15503431Scarlsonj dhcp_lif_t *lif; 15513431Scarlsonj uint_t nlifs; 15523431Scarlsonj uint_t numlifs; 15533431Scarlsonj uint_t numexp; 15540Sstevel@tonic-gate 15553431Scarlsonj numlifs = numexp = 0; 15563431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 15573431Scarlsonj lif = dlp->dl_lifs; 15583431Scarlsonj nlifs = dlp->dl_nlifs; 15593431Scarlsonj numlifs += nlifs; 15603431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 15613431Scarlsonj if (lif->lif_expired) 15623431Scarlsonj numexp++; 15633431Scarlsonj } 15640Sstevel@tonic-gate } 15653431Scarlsonj if (numlifs == 0) 15663431Scarlsonj return (DHCP_EXP_NOLIFS); 15673431Scarlsonj else if (numexp == 0) 15683431Scarlsonj return (DHCP_EXP_NOEXP); 15693431Scarlsonj else if (numlifs == numexp) 15703431Scarlsonj return (DHCP_EXP_ALLEXP); 15713431Scarlsonj else 15723431Scarlsonj return (DHCP_EXP_SOMEEXP); 15730Sstevel@tonic-gate } 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate /* 15763431Scarlsonj * find_expired_lif(): find the first expired LIF on a given state machine 15770Sstevel@tonic-gate * 15783431Scarlsonj * input: dhcp_smach_t *: the state machine to scan 15793431Scarlsonj * output: dhcp_lif_t *: the first expired LIF, or NULL if none. 15800Sstevel@tonic-gate */ 15810Sstevel@tonic-gate 15823431Scarlsonj dhcp_lif_t * 15833431Scarlsonj find_expired_lif(dhcp_smach_t *dsmp) 15840Sstevel@tonic-gate { 15853431Scarlsonj dhcp_lease_t *dlp; 15863431Scarlsonj dhcp_lif_t *lif; 15873431Scarlsonj uint_t nlifs; 15883431Scarlsonj 15893431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 15903431Scarlsonj lif = dlp->dl_lifs; 15913431Scarlsonj nlifs = dlp->dl_nlifs; 15923431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 15933431Scarlsonj if (lif->lif_expired) 15943431Scarlsonj return (lif); 15953431Scarlsonj } 15963431Scarlsonj } 15973431Scarlsonj return (NULL); 15983431Scarlsonj } 15993431Scarlsonj 16003431Scarlsonj /* 16013431Scarlsonj * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used 16023431Scarlsonj * only for DHCPv6. 16033431Scarlsonj * 16043431Scarlsonj * input: none 16053431Scarlsonj * output: none 16063431Scarlsonj */ 16073431Scarlsonj 16083431Scarlsonj void 16093431Scarlsonj remove_v6_strays(void) 16103431Scarlsonj { 16113431Scarlsonj struct lifnum lifn; 16123431Scarlsonj struct lifconf lifc; 16133431Scarlsonj struct lifreq *lifrp, *lifrmax; 16143431Scarlsonj uint_t numifs; 16153431Scarlsonj uint64_t flags; 16160Sstevel@tonic-gate 16170Sstevel@tonic-gate /* 16183431Scarlsonj * Get the approximate number of interfaces in the system. It's only 16193431Scarlsonj * approximate because the system is dynamic -- interfaces may be 16203431Scarlsonj * plumbed or unplumbed at any time. This is also the reason for the 16213431Scarlsonj * "+ 10" fudge factor: we're trying to avoid unnecessary looping. 16220Sstevel@tonic-gate */ 16233431Scarlsonj (void) memset(&lifn, 0, sizeof (lifn)); 16243431Scarlsonj lifn.lifn_family = AF_INET6; 16253431Scarlsonj lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 16263431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) { 16273431Scarlsonj dhcpmsg(MSG_ERR, 16283431Scarlsonj "remove_v6_strays: cannot read number of interfaces"); 16293431Scarlsonj numifs = 10; 16303431Scarlsonj } else { 16313431Scarlsonj numifs = lifn.lifn_count + 10; 16320Sstevel@tonic-gate } 16330Sstevel@tonic-gate 16340Sstevel@tonic-gate /* 16353431Scarlsonj * Get the interface information. We do this in a loop so that we can 16363431Scarlsonj * recover from EINVAL from the kernel -- delivered when the buffer is 16373431Scarlsonj * too small. 16380Sstevel@tonic-gate */ 16393431Scarlsonj (void) memset(&lifc, 0, sizeof (lifc)); 16403431Scarlsonj lifc.lifc_family = AF_INET6; 16413431Scarlsonj lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 16423431Scarlsonj for (;;) { 16433431Scarlsonj lifc.lifc_len = numifs * sizeof (*lifrp); 16443431Scarlsonj lifrp = realloc(lifc.lifc_buf, lifc.lifc_len); 16453431Scarlsonj if (lifrp == NULL) { 16463431Scarlsonj dhcpmsg(MSG_ERR, 16473431Scarlsonj "remove_v6_strays: cannot allocate memory"); 16483431Scarlsonj free(lifc.lifc_buf); 16493431Scarlsonj return; 16500Sstevel@tonic-gate } 16513431Scarlsonj lifc.lifc_buf = (caddr_t)lifrp; 16523431Scarlsonj errno = 0; 16533431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 && 16543431Scarlsonj lifc.lifc_len < numifs * sizeof (*lifrp)) 16550Sstevel@tonic-gate break; 16563431Scarlsonj if (errno == 0 || errno == EINVAL) { 16573431Scarlsonj numifs <<= 1; 16583431Scarlsonj } else { 16593431Scarlsonj dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF"); 16603431Scarlsonj free(lifc.lifc_buf); 16613431Scarlsonj return; 16620Sstevel@tonic-gate } 16630Sstevel@tonic-gate } 16640Sstevel@tonic-gate 16653431Scarlsonj lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp); 16663431Scarlsonj for (; lifrp < lifrmax; lifrp++) { 16670Sstevel@tonic-gate /* 16683431Scarlsonj * Get the interface flags; we're interested in the DHCP ones. 16690Sstevel@tonic-gate */ 16703431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1) 16713431Scarlsonj continue; 16723431Scarlsonj flags = lifrp->lifr_flags; 16733431Scarlsonj if (!(flags & IFF_DHCPRUNNING)) 16743431Scarlsonj continue; 16750Sstevel@tonic-gate /* 16763431Scarlsonj * If the interface has a link-local address, then we don't 16773431Scarlsonj * control it. Just remove the flag. 16780Sstevel@tonic-gate */ 16793431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1) 16803431Scarlsonj continue; 16813431Scarlsonj if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp-> 16823431Scarlsonj lifr_addr)->sin6_addr)) { 16833431Scarlsonj lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING; 16843431Scarlsonj (void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp); 16853431Scarlsonj continue; 16860Sstevel@tonic-gate } 16870Sstevel@tonic-gate /* 16883431Scarlsonj * All others are (or were) under our control. Clean up by 16893431Scarlsonj * removing them. 16900Sstevel@tonic-gate */ 16913431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) { 16923431Scarlsonj dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s", 16933431Scarlsonj lifrp->lifr_name); 16943431Scarlsonj } else if (errno != ENXIO) { 16953431Scarlsonj dhcpmsg(MSG_ERR, 16963431Scarlsonj "remove_v6_strays: SIOCLIFREMOVEIF %s", 16973431Scarlsonj lifrp->lifr_name); 16980Sstevel@tonic-gate } 16990Sstevel@tonic-gate } 17003431Scarlsonj free(lifc.lifc_buf); 17010Sstevel@tonic-gate } 1702