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 /*
228485SPeter.Memishian@Sun.COM * Copyright 2009 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>
410Sstevel@tonic-gate
423431Scarlsonj #include "agent.h"
430Sstevel@tonic-gate #include "interface.h"
440Sstevel@tonic-gate #include "util.h"
450Sstevel@tonic-gate #include "packet.h"
460Sstevel@tonic-gate #include "states.h"
473431Scarlsonj
483431Scarlsonj dhcp_pif_t *v4root;
493431Scarlsonj dhcp_pif_t *v6root;
503431Scarlsonj
513431Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu;
520Sstevel@tonic-gate
530Sstevel@tonic-gate /*
543431Scarlsonj * Interface flags to watch: things that should be under our direct control.
550Sstevel@tonic-gate */
564823Sseb #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \
574823Sseb IFF_TEMPORARY)
580Sstevel@tonic-gate
593431Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *);
600Sstevel@tonic-gate
610Sstevel@tonic-gate /*
623431Scarlsonj * insert_pif(): creates a new physical interface structure and chains it on
633431Scarlsonj * the list. Initializes state that remains consistent across
643431Scarlsonj * all use of the physical interface entry.
650Sstevel@tonic-gate *
663431Scarlsonj * input: const char *: the name of the physical interface
673431Scarlsonj * boolean_t: if B_TRUE, this is DHCPv6
683431Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
690Sstevel@tonic-gate * error code with the reason why
703431Scarlsonj * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
710Sstevel@tonic-gate */
720Sstevel@tonic-gate
733431Scarlsonj dhcp_pif_t *
insert_pif(const char * pname,boolean_t isv6,int * error)743431Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error)
750Sstevel@tonic-gate {
763431Scarlsonj dhcp_pif_t *pif;
773431Scarlsonj struct lifreq lifr;
788485SPeter.Memishian@Sun.COM lifgroupinfo_t lifgr;
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
114*11076SCathy.Zhou@Sun.COM /*
115*11076SCathy.Zhou@Sun.COM * Check if this is a VRRP interface. If yes, its IP addresses (the
116*11076SCathy.Zhou@Sun.COM * VRRP virtual addresses) cannot be configured using DHCP.
117*11076SCathy.Zhou@Sun.COM */
118*11076SCathy.Zhou@Sun.COM if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
119*11076SCathy.Zhou@Sun.COM *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
120*11076SCathy.Zhou@Sun.COM dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFFLAGS for %s", pname);
121*11076SCathy.Zhou@Sun.COM goto failure;
122*11076SCathy.Zhou@Sun.COM }
123*11076SCathy.Zhou@Sun.COM
124*11076SCathy.Zhou@Sun.COM if (lifr.lifr_flags & IFF_VRRP) {
125*11076SCathy.Zhou@Sun.COM *error = DHCP_IPC_E_INVIF;
126*11076SCathy.Zhou@Sun.COM dhcpmsg(MSG_ERROR, "insert_pif: VRRP virtual addresses over %s "
127*11076SCathy.Zhou@Sun.COM "cannot be configured using DHCP", pname);
128*11076SCathy.Zhou@Sun.COM goto failure;
129*11076SCathy.Zhou@Sun.COM }
130*11076SCathy.Zhou@Sun.COM
1315978Smeem if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) {
1325978Smeem *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
1335978Smeem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname);
1345978Smeem goto failure;
1355978Smeem }
1365978Smeem pif->pif_max = lifr.lifr_mtu;
1373431Scarlsonj
1385978Smeem if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
1395978Smeem dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to "
1405978Smeem "support DHCP (%u < %u)", pname, pif->pif_max,
1415978Smeem DHCP_DEF_MAX_SIZE);
1425978Smeem *error = DHCP_IPC_E_INVIF;
1435978Smeem goto failure;
1445978Smeem }
1455978Smeem
1465978Smeem /*
1478485SPeter.Memishian@Sun.COM * Check if the pif is in an IPMP group. Interfaces using IPMP don't
1488485SPeter.Memishian@Sun.COM * have dedicated hardware addresses, and get their hardware type from
1498485SPeter.Memishian@Sun.COM * the SIOCGLIFGROUPINFO ioctl rather than DLPI.
1505978Smeem */
1518485SPeter.Memishian@Sun.COM if (ioctl(fd, SIOCGLIFGROUPNAME, &lifr) == -1) {
1528485SPeter.Memishian@Sun.COM *error = DHCP_IPC_E_INT;
1538485SPeter.Memishian@Sun.COM dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPNAME for %s", pname);
1548485SPeter.Memishian@Sun.COM goto failure;
1558485SPeter.Memishian@Sun.COM }
1568485SPeter.Memishian@Sun.COM
1578485SPeter.Memishian@Sun.COM if (lifr.lifr_groupname[0] != '\0') {
1588485SPeter.Memishian@Sun.COM (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
1598485SPeter.Memishian@Sun.COM LIFGRNAMSIZ);
1608485SPeter.Memishian@Sun.COM if (ioctl(fd, SIOCGLIFGROUPINFO, &lifgr) == -1) {
1618485SPeter.Memishian@Sun.COM *error = DHCP_IPC_E_INT;
1628485SPeter.Memishian@Sun.COM dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPINFO for %s",
1638485SPeter.Memishian@Sun.COM lifgr.gi_grname);
1648485SPeter.Memishian@Sun.COM goto failure;
1658485SPeter.Memishian@Sun.COM }
1668485SPeter.Memishian@Sun.COM
1678485SPeter.Memishian@Sun.COM pif->pif_hwtype = dlpi_arptype(lifgr.gi_mactype);
1688485SPeter.Memishian@Sun.COM pif->pif_under_ipmp = (strcmp(pname, lifgr.gi_grifname) != 0);
1698485SPeter.Memishian@Sun.COM (void) strlcpy(pif->pif_grifname, lifgr.gi_grifname, LIFNAMSIZ);
1708485SPeter.Memishian@Sun.COM
1718485SPeter.Memishian@Sun.COM /*
1728485SPeter.Memishian@Sun.COM * For IPMP underlying interfaces, stash the interface index
1738485SPeter.Memishian@Sun.COM * of the IPMP meta-interface; we'll use it to send/receive
1748485SPeter.Memishian@Sun.COM * traffic. This is both necessary (since IP_BOUND_IF for
1758485SPeter.Memishian@Sun.COM * non-unicast traffic won't work on underlying interfaces)
1768485SPeter.Memishian@Sun.COM * and preferred (since a test address lease will be able to
1778485SPeter.Memishian@Sun.COM * be maintained as long as another interface in the group is
1788485SPeter.Memishian@Sun.COM * still functioning).
1798485SPeter.Memishian@Sun.COM */
1808485SPeter.Memishian@Sun.COM if (pif->pif_under_ipmp) {
1818485SPeter.Memishian@Sun.COM (void) strlcpy(lifr.lifr_name, pif->pif_grifname,
1828485SPeter.Memishian@Sun.COM LIFNAMSIZ);
1838485SPeter.Memishian@Sun.COM
1848485SPeter.Memishian@Sun.COM if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
1858485SPeter.Memishian@Sun.COM *error = DHCP_IPC_E_INT;
1868485SPeter.Memishian@Sun.COM dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX "
1878485SPeter.Memishian@Sun.COM "for %s", lifr.lifr_name);
1888485SPeter.Memishian@Sun.COM goto failure;
1898485SPeter.Memishian@Sun.COM }
1908485SPeter.Memishian@Sun.COM pif->pif_grindex = lifr.lifr_index;
1918485SPeter.Memishian@Sun.COM }
1928485SPeter.Memishian@Sun.COM }
1938485SPeter.Memishian@Sun.COM
1948485SPeter.Memishian@Sun.COM /*
1958485SPeter.Memishian@Sun.COM * For IPv4, if the hardware type is still unknown, use DLPI to
1968485SPeter.Memishian@Sun.COM * determine it, the hardware address, and hardware address length.
1978485SPeter.Memishian@Sun.COM */
1988485SPeter.Memishian@Sun.COM if (!isv6 && pif->pif_hwtype == 0) {
1998485SPeter.Memishian@Sun.COM int rc;
2008485SPeter.Memishian@Sun.COM dlpi_info_t dlinfo;
2015978Smeem
2024456Sss150715 if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) {
2034456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s",
2044456Sss150715 dlpi_strerror(rc));
2054456Sss150715 *error = DHCP_IPC_E_INVIF;
2064456Sss150715 goto failure;
2074456Sss150715 }
2084456Sss150715
2094456Sss150715 if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) {
2104456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s",
2114456Sss150715 dlpi_strerror(rc));
2123431Scarlsonj *error = DHCP_IPC_E_INVIF;
2133431Scarlsonj goto failure;
2143431Scarlsonj }
2153431Scarlsonj
2165978Smeem if ((rc = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) {
2174456Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s",
2184456Sss150715 dlpi_strerror(rc));
2194456Sss150715 *error = DHCP_IPC_E_INVIF;
2204456Sss150715 goto failure;
2214456Sss150715 }
2224456Sss150715
2234456Sss150715 pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype);
2244456Sss150715 pif->pif_hwlen = dlinfo.di_physaddrlen;
2253431Scarlsonj
2265381Smeem dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d",
2275381Smeem pname, pif->pif_hwtype, pif->pif_hwlen);
2283431Scarlsonj
2293431Scarlsonj if (pif->pif_hwlen > 0) {
2303431Scarlsonj pif->pif_hwaddr = malloc(pif->pif_hwlen);
2313431Scarlsonj if (pif->pif_hwaddr == NULL) {
2323431Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
2333431Scarlsonj "pif_hwaddr for %s", pname);
2343431Scarlsonj *error = DHCP_IPC_E_MEMORY;
2353431Scarlsonj goto failure;
2363431Scarlsonj }
2375381Smeem (void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr,
2385381Smeem pif->pif_hwlen);
2393431Scarlsonj }
2403431Scarlsonj
2415381Smeem dlpi_close(dh);
2425381Smeem dh = NULL;
2433431Scarlsonj }
2443431Scarlsonj
2453431Scarlsonj insque(pif, isv6 ? &v6root : &v4root);
2463431Scarlsonj
2473431Scarlsonj return (pif);
2483431Scarlsonj failure:
2495381Smeem if (dh != NULL)
2505381Smeem dlpi_close(dh);
2513431Scarlsonj release_pif(pif);
2523431Scarlsonj return (NULL);
2533431Scarlsonj }
2543431Scarlsonj
2553431Scarlsonj /*
2563431Scarlsonj * hold_pif(): acquire a hold on a physical interface structure.
2573431Scarlsonj *
2583431Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure
2593431Scarlsonj * output: none
2603431Scarlsonj */
2613431Scarlsonj
2623431Scarlsonj void
hold_pif(dhcp_pif_t * pif)2633431Scarlsonj hold_pif(dhcp_pif_t *pif)
2643431Scarlsonj {
2653431Scarlsonj pif->pif_hold_count++;
2663431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name,
2673431Scarlsonj pif->pif_hold_count);
2683431Scarlsonj }
2693431Scarlsonj
2703431Scarlsonj /*
2713431Scarlsonj * release_pif(): release a hold on a physical interface structure; will
2723431Scarlsonj * destroy the structure on the last hold removed.
2733431Scarlsonj *
2743431Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure
2753431Scarlsonj * output: none
2763431Scarlsonj */
2773431Scarlsonj
2783431Scarlsonj void
release_pif(dhcp_pif_t * pif)2793431Scarlsonj release_pif(dhcp_pif_t *pif)
2803431Scarlsonj {
2813431Scarlsonj if (pif->pif_hold_count == 0) {
2823431Scarlsonj dhcpmsg(MSG_CRIT, "release_pif: extraneous release");
2833431Scarlsonj return;
2843431Scarlsonj }
2853431Scarlsonj
2863431Scarlsonj if (--pif->pif_hold_count == 0) {
2873431Scarlsonj dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s",
2883431Scarlsonj pif->pif_name);
2893431Scarlsonj
2903431Scarlsonj remque(pif);
2913431Scarlsonj free(pif->pif_hwaddr);
2923431Scarlsonj free(pif);
2933431Scarlsonj } else {
2943431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u",
2953431Scarlsonj pif->pif_name, pif->pif_hold_count);
2963431Scarlsonj }
2973431Scarlsonj }
2983431Scarlsonj
2993431Scarlsonj /*
3003431Scarlsonj * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
3013431Scarlsonj * previous PIF pointer (or NULL for list start).
3023431Scarlsonj * Caller is expected to iterate through all
3033431Scarlsonj * potential matches to find interface of interest.
3043431Scarlsonj *
3053431Scarlsonj * input: uint16_t: the interface index (truncated)
3063431Scarlsonj * dhcp_pif_t *: the previous PIF, or NULL for list start
3073431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
3083431Scarlsonj * output: dhcp_pif_t *: the next matching PIF, or NULL if not found
3093431Scarlsonj * note: This operates using the 'truncated' (16-bit) ifindex as seen by
3103431Scarlsonj * routing socket clients. The value stored in pif_index is the
3113431Scarlsonj * 32-bit ifindex from the ioctl interface.
3123431Scarlsonj */
3133431Scarlsonj
3143431Scarlsonj dhcp_pif_t *
lookup_pif_by_uindex(uint16_t ifindex,dhcp_pif_t * pif,boolean_t isv6)3153431Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6)
3163431Scarlsonj {
3173431Scarlsonj if (pif == NULL)
3183431Scarlsonj pif = isv6 ? v6root : v4root;
3193431Scarlsonj else
3203431Scarlsonj pif = pif->pif_next;
3213431Scarlsonj
3223431Scarlsonj for (; pif != NULL; pif = pif->pif_next) {
3233431Scarlsonj if ((pif->pif_index & 0xffff) == ifindex)
3243431Scarlsonj break;
3253431Scarlsonj }
3263431Scarlsonj
3273431Scarlsonj return (pif);
3283431Scarlsonj }
3293431Scarlsonj
3303431Scarlsonj /*
3313431Scarlsonj * lookup_pif_by_name(): Looks up a physical interface entry given a name.
3323431Scarlsonj *
3333431Scarlsonj * input: const char *: the physical interface name
3343431Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
3353431Scarlsonj * output: dhcp_pif_t *: the matching PIF, or NULL if not found
3363431Scarlsonj */
3373431Scarlsonj
3383431Scarlsonj dhcp_pif_t *
lookup_pif_by_name(const char * pname,boolean_t isv6)3393431Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6)
3403431Scarlsonj {
3413431Scarlsonj dhcp_pif_t *pif;
3423431Scarlsonj
3433431Scarlsonj pif = isv6 ? v6root : v4root;
3443431Scarlsonj
3453431Scarlsonj for (; pif != NULL; pif = pif->pif_next) {
3463431Scarlsonj if (strcmp(pif->pif_name, pname) == 0)
3473431Scarlsonj break;
3483431Scarlsonj }
3493431Scarlsonj
3503431Scarlsonj return (pif);
3513431Scarlsonj }
3523431Scarlsonj
3533431Scarlsonj /*
3543431Scarlsonj * pif_status(): update the physical interface up/down status.
3553431Scarlsonj *
3565381Smeem * input: dhcp_pif_t *: the physical interface to be updated
3573431Scarlsonj * boolean_t: B_TRUE if the interface is going up
3583431Scarlsonj * output: none
3593431Scarlsonj */
3603431Scarlsonj
3613431Scarlsonj void
pif_status(dhcp_pif_t * pif,boolean_t isup)3623431Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup)
3633431Scarlsonj {
3643431Scarlsonj dhcp_lif_t *lif;
3653431Scarlsonj dhcp_smach_t *dsmp;
3663431Scarlsonj
3673431Scarlsonj pif->pif_running = isup;
3684516Scarlsonj dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name,
3693431Scarlsonj isup ? "come back up" : "gone down");
3703431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
3713431Scarlsonj for (dsmp = lif->lif_smachs; dsmp != NULL;
3723431Scarlsonj dsmp = dsmp->dsm_next) {
3733431Scarlsonj if (isup)
3743431Scarlsonj refresh_smach(dsmp);
3753431Scarlsonj else
3763431Scarlsonj remove_default_routes(dsmp);
3773431Scarlsonj }
3783431Scarlsonj }
3793431Scarlsonj }
3803431Scarlsonj
3813431Scarlsonj /* Helper for insert_lif: extract addresses as defined */
3823431Scarlsonj #define ASSIGN_ADDR(v4, v6, lf) \
3833431Scarlsonj if (pif->pif_isv6) { \
3843431Scarlsonj lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \
3853431Scarlsonj } else { \
3863431Scarlsonj lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \
3873431Scarlsonj }
3883431Scarlsonj
3893431Scarlsonj /*
3903431Scarlsonj * insert_lif(): Creates a new logical interface structure and chains it on
3913431Scarlsonj * the list for a given physical interface. Initializes state
3923431Scarlsonj * that remains consistent across all use of the logical
3933431Scarlsonj * interface entry. Caller's PIF hold is transferred to the
3943431Scarlsonj * LIF on success, and is dropped on failure.
3953431Scarlsonj *
3963431Scarlsonj * input: dhcp_pif_t *: pointer to the physical interface for this LIF
3973431Scarlsonj * const char *: the name of the logical interface
3983431Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
3993431Scarlsonj * error code with the reason why
4003431Scarlsonj * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
4013431Scarlsonj */
4023431Scarlsonj
4033431Scarlsonj dhcp_lif_t *
insert_lif(dhcp_pif_t * pif,const char * lname,int * error)4043431Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error)
4053431Scarlsonj {
4063431Scarlsonj dhcp_lif_t *lif;
4073431Scarlsonj int fd;
4083431Scarlsonj struct lifreq lifr;
4093431Scarlsonj
4103431Scarlsonj if ((lif = calloc(1, sizeof (*lif))) == NULL) {
4113431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for "
4123431Scarlsonj "%s", lname);
4130Sstevel@tonic-gate *error = DHCP_IPC_E_MEMORY;
4140Sstevel@tonic-gate return (NULL);
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate
4173431Scarlsonj lif->lif_sock_ip_fd = -1;
4185381Smeem lif->lif_packet_id = -1;
4193431Scarlsonj lif->lif_iaid_id = -1;
4203431Scarlsonj lif->lif_hold_count = 1;
4213431Scarlsonj lif->lif_pif = pif;
4223431Scarlsonj lif->lif_removed = B_TRUE;
4233431Scarlsonj init_timer(&lif->lif_preferred, 0);
4243431Scarlsonj init_timer(&lif->lif_expire, 0);
4250Sstevel@tonic-gate
4263431Scarlsonj if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) {
4273431Scarlsonj dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long",
4283431Scarlsonj lname);
4290Sstevel@tonic-gate *error = DHCP_IPC_E_INVIF;
4300Sstevel@tonic-gate goto failure;
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate
4333431Scarlsonj (void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ);
4343431Scarlsonj
4353431Scarlsonj fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
4360Sstevel@tonic-gate
4373431Scarlsonj if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1)
4383431Scarlsonj lif->lif_max = 1024;
4393431Scarlsonj else
4403431Scarlsonj lif->lif_max = lifr.lifr_mtu;
4410Sstevel@tonic-gate
4423431Scarlsonj if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) {
4433431Scarlsonj if (errno == ENXIO)
4443431Scarlsonj *error = DHCP_IPC_E_INVIF;
4453431Scarlsonj else
4463431Scarlsonj *error = DHCP_IPC_E_INT;
4473431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname);
4480Sstevel@tonic-gate goto failure;
4490Sstevel@tonic-gate }
4503431Scarlsonj ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr);
4510Sstevel@tonic-gate
4523431Scarlsonj if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
4532546Scarlsonj if (errno == ENXIO)
4542546Scarlsonj *error = DHCP_IPC_E_INVIF;
4552546Scarlsonj else
4562546Scarlsonj *error = DHCP_IPC_E_INT;
4573431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname);
4583431Scarlsonj goto failure;
4593431Scarlsonj }
4603431Scarlsonj ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr);
4613431Scarlsonj
4623431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
4633431Scarlsonj *error = DHCP_IPC_E_INT;
4643431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname);
4652546Scarlsonj goto failure;
4662546Scarlsonj }
4673431Scarlsonj lif->lif_flags = lifr.lifr_flags;
4683431Scarlsonj
4693431Scarlsonj /*
4703431Scarlsonj * If we've just detected the interface going up or down, then signal
4713431Scarlsonj * an appropriate action. There may be other state machines here.
4723431Scarlsonj */
4733431Scarlsonj if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) {
4743431Scarlsonj pif_status(pif, B_TRUE);
4753431Scarlsonj } else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) {
4763431Scarlsonj pif_status(pif, B_FALSE);
4773431Scarlsonj }
4783431Scarlsonj
4793431Scarlsonj if (lifr.lifr_flags & IFF_POINTOPOINT) {
4803431Scarlsonj if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) {
4813431Scarlsonj *error = DHCP_IPC_E_INT;
4823431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s",
4833431Scarlsonj lname);
4843431Scarlsonj goto failure;
4853431Scarlsonj }
4863431Scarlsonj ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr);
4873431Scarlsonj } else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) {
4883431Scarlsonj if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) {
4893431Scarlsonj *error = DHCP_IPC_E_INT;
4903431Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s",
4913431Scarlsonj lname);
4923431Scarlsonj goto failure;
4933431Scarlsonj }
4943431Scarlsonj lif->lif_broadcast =
4953431Scarlsonj ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr.
4963431Scarlsonj s_addr;
4973431Scarlsonj }
4983431Scarlsonj
4993431Scarlsonj if (pif->pif_isv6)
5003431Scarlsonj cached_v6_max_mtu = 0;
5013431Scarlsonj else
5023431Scarlsonj cached_v4_max_mtu = 0;
5033431Scarlsonj
5043431Scarlsonj lif->lif_removed = B_FALSE;
5053431Scarlsonj insque(lif, &pif->pif_lifs);
5063431Scarlsonj
5073431Scarlsonj return (lif);
5083431Scarlsonj
5093431Scarlsonj failure:
5103431Scarlsonj release_lif(lif);
5113431Scarlsonj return (NULL);
5123431Scarlsonj }
5133431Scarlsonj
5143431Scarlsonj /*
5153431Scarlsonj * hold_lif(): acquire a hold on a logical interface structure.
5163431Scarlsonj *
5173431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure
5183431Scarlsonj * output: none
5193431Scarlsonj */
5203431Scarlsonj
5213431Scarlsonj void
hold_lif(dhcp_lif_t * lif)5223431Scarlsonj hold_lif(dhcp_lif_t *lif)
5233431Scarlsonj {
5243431Scarlsonj lif->lif_hold_count++;
5253431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name,
5263431Scarlsonj lif->lif_hold_count);
5273431Scarlsonj }
5283431Scarlsonj
5293431Scarlsonj /*
5303431Scarlsonj * release_lif(): release a hold on a logical interface structure; will
5313431Scarlsonj * destroy the structure on the last hold removed.
5323431Scarlsonj *
5333431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure
5343431Scarlsonj * output: none
5353431Scarlsonj */
5363431Scarlsonj
5373431Scarlsonj void
release_lif(dhcp_lif_t * lif)5383431Scarlsonj release_lif(dhcp_lif_t *lif)
5393431Scarlsonj {
5403431Scarlsonj if (lif->lif_hold_count == 0) {
5413431Scarlsonj dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s",
5423431Scarlsonj lif->lif_name);
5433431Scarlsonj return;
5443431Scarlsonj }
5453431Scarlsonj
5463431Scarlsonj if (lif->lif_hold_count == 1 && !lif->lif_removed) {
5473431Scarlsonj unplumb_lif(lif);
5483431Scarlsonj return;
5493431Scarlsonj }
5503431Scarlsonj
5513431Scarlsonj if (--lif->lif_hold_count == 0) {
5523431Scarlsonj dhcp_pif_t *pif;
5533431Scarlsonj
5543431Scarlsonj dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s",
5553431Scarlsonj lif->lif_name);
5563431Scarlsonj
5573431Scarlsonj if (lif->lif_lease != NULL)
5583431Scarlsonj dhcpmsg(MSG_CRIT,
5593431Scarlsonj "release_lif: still holding lease at last hold!");
5603431Scarlsonj close_ip_lif(lif);
5613431Scarlsonj pif = lif->lif_pif;
5623431Scarlsonj if (pif->pif_isv6)
5633431Scarlsonj cached_v6_max_mtu = 0;
5643431Scarlsonj else
5653431Scarlsonj cached_v4_max_mtu = 0;
5663431Scarlsonj release_pif(pif);
5673431Scarlsonj free(lif);
5683431Scarlsonj } else {
5693431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u",
5703431Scarlsonj lif->lif_name, lif->lif_hold_count);
5713431Scarlsonj }
5723431Scarlsonj }
5733431Scarlsonj
5743431Scarlsonj /*
5753431Scarlsonj * remove_lif(): remove a logical interface from its PIF and lease (if any) and
5763431Scarlsonj * the lease's hold on the LIF. Assumes that we did not plumb
5773431Scarlsonj * the interface.
5783431Scarlsonj *
5793431Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure
5803431Scarlsonj * output: none
5813431Scarlsonj */
5823431Scarlsonj
5833431Scarlsonj void
remove_lif(dhcp_lif_t * lif)5843431Scarlsonj remove_lif(dhcp_lif_t *lif)
5853431Scarlsonj {
5863431Scarlsonj if (lif->lif_plumbed) {
5873431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s",
5883431Scarlsonj lif->lif_name);
5893431Scarlsonj return;
5903431Scarlsonj }
5913431Scarlsonj if (lif->lif_removed) {
5923431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s",
5933431Scarlsonj lif->lif_name);
5943431Scarlsonj } else {
5953431Scarlsonj dhcp_lif_t *lifnext;
5963431Scarlsonj dhcp_lease_t *dlp;
5973431Scarlsonj
5983431Scarlsonj dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name);
5993431Scarlsonj lif->lif_removed = B_TRUE;
6003431Scarlsonj lifnext = lif->lif_next;
6013431Scarlsonj clear_lif_dhcp(lif);
6023431Scarlsonj cancel_lif_timers(lif);
6033431Scarlsonj if (lif->lif_iaid_id != -1 &&
6043431Scarlsonj iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) {
6053431Scarlsonj lif->lif_iaid_id = -1;
6063431Scarlsonj release_lif(lif);
6073431Scarlsonj }
6083431Scarlsonj
6093431Scarlsonj /* Remove from PIF list */
6103431Scarlsonj remque(lif);
6113431Scarlsonj
6123431Scarlsonj /* If we were part of a lease, then remove ourselves */
6133431Scarlsonj if ((dlp = lif->lif_lease) != NULL) {
6143431Scarlsonj if (--dlp->dl_nlifs == 0)
6153431Scarlsonj dlp->dl_lifs = NULL;
6163431Scarlsonj else if (dlp->dl_lifs == lif)
6173431Scarlsonj dlp->dl_lifs = lifnext;
6183431Scarlsonj if (lif->lif_declined != NULL) {
6193431Scarlsonj dlp->dl_smach->dsm_lif_down--;
6203431Scarlsonj lif->lif_declined = NULL;
6213431Scarlsonj }
6229629Sjames.d.carlson@sun.com if (lif->lif_dad_wait) {
6239629Sjames.d.carlson@sun.com lif->lif_dad_wait = _B_FALSE;
6249629Sjames.d.carlson@sun.com dlp->dl_smach->dsm_lif_wait--;
6259629Sjames.d.carlson@sun.com }
6263431Scarlsonj lif->lif_lease = NULL;
6273431Scarlsonj release_lif(lif);
6283431Scarlsonj }
6293431Scarlsonj }
6303431Scarlsonj }
6313431Scarlsonj
6323431Scarlsonj /*
6333431Scarlsonj * lookup_lif_by_name(): Looks up a logical interface entry given a name and
6343431Scarlsonj * a physical interface.
6353431Scarlsonj *
6363431Scarlsonj * input: const char *: the logical interface name
6373431Scarlsonj * const dhcp_pif_t *: the physical interface
6383431Scarlsonj * output: dhcp_lif_t *: the matching LIF, or NULL if not found
6393431Scarlsonj */
6403431Scarlsonj
6413431Scarlsonj dhcp_lif_t *
lookup_lif_by_name(const char * lname,const dhcp_pif_t * pif)6423431Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif)
6433431Scarlsonj {
6443431Scarlsonj dhcp_lif_t *lif;
6453431Scarlsonj
6463431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
6473431Scarlsonj if (strcmp(lif->lif_name, lname) == 0)
6483431Scarlsonj break;
6493431Scarlsonj }
6503431Scarlsonj
6513431Scarlsonj return (lif);
6523431Scarlsonj }
6533431Scarlsonj
6543431Scarlsonj /*
6553431Scarlsonj * checkaddr(): checks if the given address is still set on the given LIF
6563431Scarlsonj *
6573431Scarlsonj * input: const dhcp_lif_t *: the LIF to check
6583431Scarlsonj * int: the address to look up on the interface (ioctl)
6593431Scarlsonj * const in6_addr_t *: the address to compare to
6603431Scarlsonj * const char *: name of the address for logging purposes
6613431Scarlsonj * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
6623431Scarlsonj */
6633431Scarlsonj
6643431Scarlsonj static boolean_t
checkaddr(const dhcp_lif_t * lif,int ioccmd,const in6_addr_t * addr,const char * aname)6653431Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr,
6663431Scarlsonj const char *aname)
6673431Scarlsonj {
6683431Scarlsonj boolean_t isv6;
6693431Scarlsonj int fd;
6703431Scarlsonj struct lifreq lifr;
6715381Smeem char abuf1[INET6_ADDRSTRLEN];
6725381Smeem char abuf2[INET6_ADDRSTRLEN];
6733431Scarlsonj
6743431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq));
6753431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
6763431Scarlsonj
6773431Scarlsonj isv6 = lif->lif_pif->pif_isv6;
6783431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd;
6793431Scarlsonj
6803431Scarlsonj if (ioctl(fd, ioccmd, &lifr) == -1) {
6813431Scarlsonj if (errno == ENXIO) {
6823431Scarlsonj dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone",
6833431Scarlsonj lif->lif_name);
6843431Scarlsonj return (B_FALSE);
6853431Scarlsonj }
6863431Scarlsonj dhcpmsg(MSG_DEBUG,
6873431Scarlsonj "checkaddr: ignoring ioctl error on %s %x: %s",
6883431Scarlsonj lif->lif_name, ioccmd, strerror(errno));
6893431Scarlsonj } else if (isv6) {
6903431Scarlsonj struct sockaddr_in6 *sin6 =
6913431Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr;
6923431Scarlsonj
6933431Scarlsonj if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) {
6943431Scarlsonj dhcpmsg(MSG_WARNING,
6955381Smeem "checkaddr: expected %s %s on %s, have %s", aname,
6965381Smeem inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)),
6975381Smeem lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr,
6985381Smeem abuf2, sizeof (abuf2)));
6993431Scarlsonj return (B_FALSE);
7003431Scarlsonj }
7013431Scarlsonj } else {
7023431Scarlsonj struct sockaddr_in *sinp =
7033431Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr;
7043431Scarlsonj ipaddr_t v4addr;
7053431Scarlsonj
7063431Scarlsonj IN6_V4MAPPED_TO_IPADDR(addr, v4addr);
7073431Scarlsonj if (sinp->sin_addr.s_addr != v4addr) {
7083431Scarlsonj dhcpmsg(MSG_WARNING,
7095381Smeem "checkaddr: expected %s %s on %s, have %s", aname,
7105381Smeem inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)),
7115381Smeem lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr,
7125381Smeem abuf2, sizeof (abuf2)));
7133431Scarlsonj return (B_FALSE);
7143431Scarlsonj }
7153431Scarlsonj }
7163431Scarlsonj return (B_TRUE);
7173431Scarlsonj }
7183431Scarlsonj
7193431Scarlsonj /*
7203431Scarlsonj * verify_lif(): verifies than a LIF is still valid (i.e., has not been
7213431Scarlsonj * explicitly or implicitly dropped or released)
7223431Scarlsonj *
7233431Scarlsonj * input: const dhcp_lif_t *: the LIF to verify
7243431Scarlsonj * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
7253431Scarlsonj */
7263431Scarlsonj
7273431Scarlsonj boolean_t
verify_lif(const dhcp_lif_t * lif)7283431Scarlsonj verify_lif(const dhcp_lif_t *lif)
7293431Scarlsonj {
7303431Scarlsonj boolean_t isv6;
7313431Scarlsonj int fd;
7323431Scarlsonj struct lifreq lifr;
7338485SPeter.Memishian@Sun.COM dhcp_pif_t *pif = lif->lif_pif;
7343431Scarlsonj
7353431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq));
7363431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
7373431Scarlsonj
7388485SPeter.Memishian@Sun.COM isv6 = pif->pif_isv6;
7393431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd;
7403431Scarlsonj
7413431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
7424106Scarlsonj if (errno != ENXIO) {
7434106Scarlsonj dhcpmsg(MSG_ERR,
7444106Scarlsonj "verify_lif: SIOCGLIFFLAGS failed on %s",
7454106Scarlsonj lif->lif_name);
7464106Scarlsonj }
7473431Scarlsonj return (B_FALSE);
7483431Scarlsonj }
7493431Scarlsonj
7503431Scarlsonj /*
7513431Scarlsonj * If important flags have changed, then abandon the interface.
7523431Scarlsonj */
7533431Scarlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) {
7543431Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: "
7553431Scarlsonj "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags,
7563431Scarlsonj lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) &
7573431Scarlsonj DHCP_IFF_WATCH);
7583431Scarlsonj return (B_FALSE);
7593431Scarlsonj }
7603431Scarlsonj
7613431Scarlsonj /*
7628485SPeter.Memishian@Sun.COM * Check for delete and recreate.
7633431Scarlsonj */
7648485SPeter.Memishian@Sun.COM if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
7658485SPeter.Memishian@Sun.COM if (errno != ENXIO) {
7668485SPeter.Memishian@Sun.COM dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed "
7678485SPeter.Memishian@Sun.COM "on %s", lif->lif_name);
7688485SPeter.Memishian@Sun.COM }
7698485SPeter.Memishian@Sun.COM return (B_FALSE);
7703431Scarlsonj }
7718485SPeter.Memishian@Sun.COM if (lifr.lifr_index != pif->pif_index) {
7728485SPeter.Memishian@Sun.COM dhcpmsg(MSG_DEBUG,
7738485SPeter.Memishian@Sun.COM "verify_lif: ifindex on %s changed: %u to %u",
7748485SPeter.Memishian@Sun.COM lif->lif_name, pif->pif_index, lifr.lifr_index);
7753431Scarlsonj return (B_FALSE);
7763431Scarlsonj }
7773431Scarlsonj
7788485SPeter.Memishian@Sun.COM if (pif->pif_under_ipmp) {
7798485SPeter.Memishian@Sun.COM (void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
7808485SPeter.Memishian@Sun.COM
7818485SPeter.Memishian@Sun.COM if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
7828485SPeter.Memishian@Sun.COM if (errno != ENXIO) {
7838485SPeter.Memishian@Sun.COM dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX "
7848485SPeter.Memishian@Sun.COM "failed on %s", lifr.lifr_name);
7858485SPeter.Memishian@Sun.COM }
7868485SPeter.Memishian@Sun.COM return (B_FALSE);
7878485SPeter.Memishian@Sun.COM }
7888485SPeter.Memishian@Sun.COM
7898485SPeter.Memishian@Sun.COM if (lifr.lifr_index != pif->pif_grindex) {
7908485SPeter.Memishian@Sun.COM dhcpmsg(MSG_DEBUG, "verify_lif: IPMP group ifindex "
7918485SPeter.Memishian@Sun.COM "on %s changed: %u to %u", lifr.lifr_name,
7928485SPeter.Memishian@Sun.COM pif->pif_grindex, lifr.lifr_index);
7938485SPeter.Memishian@Sun.COM return (B_FALSE);
7948485SPeter.Memishian@Sun.COM }
7953431Scarlsonj }
7963431Scarlsonj
7973431Scarlsonj /*
7983431Scarlsonj * If the IP address, netmask, or broadcast address have changed, or
7993431Scarlsonj * the interface has been unplumbed, then we act like there has been an
8003431Scarlsonj * implicit drop. (Note that the netmask is under DHCP control for
8013431Scarlsonj * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
8023431Scarlsonj * addresses.)
8033431Scarlsonj */
8042546Scarlsonj
8053431Scarlsonj if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address"))
8063431Scarlsonj return (B_FALSE);
8073431Scarlsonj
8083431Scarlsonj if (isv6) {
8093431Scarlsonj /*
8103431Scarlsonj * If it's not point-to-point, we're done. If it is, then
8113431Scarlsonj * check the peer's address as well.
8123431Scarlsonj */
8133431Scarlsonj return (!(lif->lif_flags & IFF_POINTOPOINT) ||
8143431Scarlsonj checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer,
8153431Scarlsonj "peer address"));
8163431Scarlsonj } else {
8173431Scarlsonj if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask,
8183431Scarlsonj "netmask"))
8193431Scarlsonj return (B_FALSE);
8203431Scarlsonj
8213431Scarlsonj return (checkaddr(lif,
8223431Scarlsonj (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR :
8233431Scarlsonj SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address"));
8243431Scarlsonj }
8253431Scarlsonj }
8263431Scarlsonj
8273431Scarlsonj /*
8283431Scarlsonj * canonize_lif(): puts the interface in a canonical (zeroed) form. This is
8293431Scarlsonj * used only on the "main" LIF for IPv4. All other interfaces
8303431Scarlsonj * are under dhcpagent control and are removed using
8313431Scarlsonj * unplumb_lif().
8323431Scarlsonj *
8333431Scarlsonj * input: dhcp_lif_t *: the interface to canonize
8345381Smeem * boolean_t: only canonize lif if it's under DHCP control
8353431Scarlsonj * output: none
8363431Scarlsonj */
8373431Scarlsonj
8383431Scarlsonj static void
canonize_lif(dhcp_lif_t * lif,boolean_t dhcponly)8395381Smeem canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly)
8403431Scarlsonj {
8413431Scarlsonj boolean_t isv6;
8423431Scarlsonj int fd;
8433431Scarlsonj struct lifreq lifr;
8443431Scarlsonj
8453431Scarlsonj /*
8463431Scarlsonj * If there's nothing here, then don't touch the interface. This can
8473431Scarlsonj * happen when an already-canonized LIF is recanonized.
8483431Scarlsonj */
8493431Scarlsonj if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr))
8503431Scarlsonj return;
8513431Scarlsonj
8523431Scarlsonj isv6 = lif->lif_pif->pif_isv6;
8533431Scarlsonj dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s",
8543431Scarlsonj isv6 ? 6 : 4, lif->lif_name);
8553431Scarlsonj
8563431Scarlsonj lif->lif_v6addr = my_in6addr_any;
8573431Scarlsonj lif->lif_v6mask = my_in6addr_any;
8583431Scarlsonj lif->lif_v6peer = my_in6addr_any;
8593431Scarlsonj
8603431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq));
8613431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
8623431Scarlsonj
8633431Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd;
8643431Scarlsonj
8653431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
8664106Scarlsonj if (errno != ENXIO) {
8674106Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s",
8684106Scarlsonj lif->lif_name);
8694106Scarlsonj }
8703431Scarlsonj return;
8713431Scarlsonj }
8726637Smeem lif->lif_flags = lifr.lifr_flags;
8733431Scarlsonj
8745381Smeem if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) {
8753431Scarlsonj dhcpmsg(MSG_INFO,
8763431Scarlsonj "canonize_lif: cannot clear %s; flags are %llx",
8773431Scarlsonj lif->lif_name, lifr.lifr_flags);
8783431Scarlsonj return;
8793431Scarlsonj }
8803431Scarlsonj
8813431Scarlsonj (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
8823431Scarlsonj if (isv6) {
8833431Scarlsonj struct sockaddr_in6 *sin6 =
8843431Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr;
8853431Scarlsonj
8863431Scarlsonj sin6->sin6_family = AF_INET6;
8873431Scarlsonj sin6->sin6_addr = my_in6addr_any;
8883431Scarlsonj } else {
8893431Scarlsonj struct sockaddr_in *sinv =
8903431Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr;
8913431Scarlsonj
8923431Scarlsonj sinv->sin_family = AF_INET;
8933431Scarlsonj sinv->sin_addr.s_addr = htonl(INADDR_ANY);
8943431Scarlsonj }
8953431Scarlsonj
8963431Scarlsonj if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) {
8973431Scarlsonj dhcpmsg(MSG_ERR,
8983431Scarlsonj "canonize_lif: can't clear local address on %s",
8993431Scarlsonj lif->lif_name);
9003431Scarlsonj }
9013431Scarlsonj
9029629Sjames.d.carlson@sun.com /* Clearing the address means that we're no longer waiting on DAD */
9039629Sjames.d.carlson@sun.com if (lif->lif_dad_wait) {
9049629Sjames.d.carlson@sun.com lif->lif_dad_wait = _B_FALSE;
9059629Sjames.d.carlson@sun.com lif->lif_lease->dl_smach->dsm_lif_wait--;
9069629Sjames.d.carlson@sun.com }
9079629Sjames.d.carlson@sun.com
9083431Scarlsonj if (lif->lif_flags & IFF_POINTOPOINT) {
9093431Scarlsonj if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) {
9103431Scarlsonj dhcpmsg(MSG_ERR,
9113431Scarlsonj "canonize_lif: can't clear remote address on %s",
9123431Scarlsonj lif->lif_name);
9133431Scarlsonj }
9143431Scarlsonj } else if (!isv6) {
9153431Scarlsonj if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) {
9163431Scarlsonj dhcpmsg(MSG_ERR,
9173431Scarlsonj "canonize_lif: can't clear broadcast address on %s",
9183431Scarlsonj lif->lif_name);
9193431Scarlsonj }
9203431Scarlsonj }
9218403SAnurag.Maskey@Sun.COM
9228403SAnurag.Maskey@Sun.COM /*
9238403SAnurag.Maskey@Sun.COM * Clear the netmask last as it has to be refetched after clearing.
9248403SAnurag.Maskey@Sun.COM * Netmask is under in.ndpd control with IPv6.
9258403SAnurag.Maskey@Sun.COM */
9268403SAnurag.Maskey@Sun.COM if (!isv6) {
9278403SAnurag.Maskey@Sun.COM /* Clear the netmask */
9288403SAnurag.Maskey@Sun.COM if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) {
9298403SAnurag.Maskey@Sun.COM dhcpmsg(MSG_ERR,
9308403SAnurag.Maskey@Sun.COM "canonize_lif: can't clear netmask on %s",
9318403SAnurag.Maskey@Sun.COM lif->lif_name);
9328403SAnurag.Maskey@Sun.COM } else {
9338403SAnurag.Maskey@Sun.COM /*
9348403SAnurag.Maskey@Sun.COM * When the netmask is cleared, the kernel actually sets
9358403SAnurag.Maskey@Sun.COM * the netmask to 255.0.0.0. So, refetch that netmask.
9368403SAnurag.Maskey@Sun.COM */
9378403SAnurag.Maskey@Sun.COM if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
9388403SAnurag.Maskey@Sun.COM dhcpmsg(MSG_ERR,
9398403SAnurag.Maskey@Sun.COM "canonize_lif: can't reload cleared "
9408403SAnurag.Maskey@Sun.COM "netmask on %s", lif->lif_name);
9418403SAnurag.Maskey@Sun.COM } else {
9428403SAnurag.Maskey@Sun.COM /* Refetch succeeded, update LIF */
9438403SAnurag.Maskey@Sun.COM lif->lif_netmask =
9448403SAnurag.Maskey@Sun.COM ((struct sockaddr_in *)&lifr.lifr_addr)->
9458403SAnurag.Maskey@Sun.COM sin_addr.s_addr;
9468403SAnurag.Maskey@Sun.COM }
9478403SAnurag.Maskey@Sun.COM }
9488403SAnurag.Maskey@Sun.COM }
9493431Scarlsonj }
9503431Scarlsonj
9513431Scarlsonj /*
9523431Scarlsonj * plumb_lif(): Adds the LIF to the system. This is used for all
9533431Scarlsonj * DHCPv6-derived interfaces. The returned LIF has a hold
9549629Sjames.d.carlson@sun.com * on it. The caller (configure_v6_leases) deals with the DAD
9559629Sjames.d.carlson@sun.com * wait counters.
9563431Scarlsonj *
9573431Scarlsonj * input: dhcp_lif_t *: the interface to unplumb
9583431Scarlsonj * output: none
9593431Scarlsonj */
9603431Scarlsonj
9613431Scarlsonj dhcp_lif_t *
plumb_lif(dhcp_pif_t * pif,const in6_addr_t * addr)9623431Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr)
9633431Scarlsonj {
9643431Scarlsonj dhcp_lif_t *lif;
9653431Scarlsonj char abuf[INET6_ADDRSTRLEN];
9663431Scarlsonj struct lifreq lifr;
9673431Scarlsonj struct sockaddr_in6 *sin6;
9683431Scarlsonj int error;
9693431Scarlsonj
9703431Scarlsonj (void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf));
9713431Scarlsonj
9723431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
9733431Scarlsonj if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) {
9743431Scarlsonj dhcpmsg(MSG_ERR,
9753431Scarlsonj "plumb_lif: entry for %s already exists!", abuf);
9763431Scarlsonj return (NULL);
9773431Scarlsonj }
9783431Scarlsonj }
9793431Scarlsonj
9803431Scarlsonj /* First, create a new zero-address logical interface */
9813431Scarlsonj (void) memset(&lifr, 0, sizeof (lifr));
9823431Scarlsonj (void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name));
9833431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) {
9843431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name);
9853431Scarlsonj return (NULL);
9863431Scarlsonj }
9873431Scarlsonj
9883431Scarlsonj /* Next, set the netmask to all ones */
9893431Scarlsonj sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
9903431Scarlsonj sin6->sin6_family = AF_INET6;
9913431Scarlsonj (void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr));
9923431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
9933431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s",
9943431Scarlsonj lifr.lifr_name);
9950Sstevel@tonic-gate goto failure;
9960Sstevel@tonic-gate }
9970Sstevel@tonic-gate
9983431Scarlsonj /* Now set the interface address */
9993431Scarlsonj sin6->sin6_addr = *addr;
10003431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
10013431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s",
10023431Scarlsonj lifr.lifr_name, abuf);
10033431Scarlsonj goto failure;
10043431Scarlsonj }
10053431Scarlsonj
10063431Scarlsonj /* Mark the interface up */
10073431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
10083431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s",
10093431Scarlsonj lifr.lifr_name);
10103431Scarlsonj goto failure;
10113431Scarlsonj }
10128485SPeter.Memishian@Sun.COM
10138485SPeter.Memishian@Sun.COM /*
10148485SPeter.Memishian@Sun.COM * See comment in set_lif_dhcp().
10158485SPeter.Memishian@Sun.COM */
10168485SPeter.Memishian@Sun.COM if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
10178485SPeter.Memishian@Sun.COM lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
10188485SPeter.Memishian@Sun.COM
10193431Scarlsonj lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING;
10203431Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
10213431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s",
10223431Scarlsonj lifr.lifr_name);
10233431Scarlsonj goto failure;
10243431Scarlsonj }
10253431Scarlsonj
10263431Scarlsonj /* Now we can create the internal LIF structure */
10273431Scarlsonj hold_pif(pif);
10283431Scarlsonj if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL)
10293431Scarlsonj goto failure;
10303431Scarlsonj
10313431Scarlsonj dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf,
10323431Scarlsonj lif->lif_name);
10333431Scarlsonj lif->lif_plumbed = B_TRUE;
10343431Scarlsonj
10353431Scarlsonj return (lif);
10363431Scarlsonj
10373431Scarlsonj failure:
10383431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
10393431Scarlsonj errno != ENXIO) {
10403431Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s",
10413431Scarlsonj lifr.lifr_name);
10423431Scarlsonj }
10433431Scarlsonj return (NULL);
10443431Scarlsonj }
10453431Scarlsonj
10463431Scarlsonj /*
10473431Scarlsonj * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used
10483431Scarlsonj * for all interfaces configured by DHCP (those in leases).
10493431Scarlsonj *
10503431Scarlsonj * input: dhcp_lif_t *: the interface to unplumb
10513431Scarlsonj * output: none
10523431Scarlsonj */
10533431Scarlsonj
10543431Scarlsonj void
unplumb_lif(dhcp_lif_t * lif)10553431Scarlsonj unplumb_lif(dhcp_lif_t *lif)
10563431Scarlsonj {
10573431Scarlsonj dhcp_lease_t *dlp;
10583431Scarlsonj
10593431Scarlsonj if (lif->lif_plumbed) {
10603431Scarlsonj struct lifreq lifr;
10613431Scarlsonj
10623431Scarlsonj (void) memset(&lifr, 0, sizeof (lifr));
10633431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name,
10643431Scarlsonj sizeof (lifr.lifr_name));
10653431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
10663431Scarlsonj errno != ENXIO) {
10673431Scarlsonj dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s",
10683431Scarlsonj lif->lif_name);
10693431Scarlsonj }
10703431Scarlsonj lif->lif_plumbed = B_FALSE;
10713431Scarlsonj }
10725978Smeem
10733431Scarlsonj /*
10743431Scarlsonj * Special case: if we're "unplumbing" the main LIF for DHCPv4, then
10759629Sjames.d.carlson@sun.com * just canonize it and remove it from the lease. The DAD wait flags
10769629Sjames.d.carlson@sun.com * are handled by canonize_lif or by remove_lif.
10773431Scarlsonj */
10783431Scarlsonj if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) {
10795381Smeem canonize_lif(lif, B_TRUE);
10803431Scarlsonj cancel_lif_timers(lif);
10813431Scarlsonj if (lif->lif_declined != NULL) {
10823431Scarlsonj dlp->dl_smach->dsm_lif_down--;
10833431Scarlsonj lif->lif_declined = NULL;
10843431Scarlsonj }
10853431Scarlsonj dlp->dl_nlifs = 0;
10863431Scarlsonj dlp->dl_lifs = NULL;
10873431Scarlsonj lif->lif_lease = NULL;
10883431Scarlsonj release_lif(lif);
10893431Scarlsonj } else {
10903431Scarlsonj remove_lif(lif);
10913431Scarlsonj }
10923431Scarlsonj }
10933431Scarlsonj
10943431Scarlsonj /*
10953431Scarlsonj * attach_lif(): create a new logical interface, creating the physical
10963431Scarlsonj * interface as necessary.
10973431Scarlsonj *
10983431Scarlsonj * input: const char *: the logical interface name
10993431Scarlsonj * boolean_t: B_TRUE for IPv6
11003431Scarlsonj * int *: set to DHCP_IPC_E_* if creation fails
11013431Scarlsonj * output: dhcp_lif_t *: pointer to new entry, or NULL on failure
11023431Scarlsonj */
11033431Scarlsonj
11043431Scarlsonj dhcp_lif_t *
attach_lif(const char * lname,boolean_t isv6,int * error)11053431Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error)
11063431Scarlsonj {
11073431Scarlsonj dhcp_pif_t *pif;
11083431Scarlsonj char pname[LIFNAMSIZ], *cp;
11093431Scarlsonj
11103431Scarlsonj (void) strlcpy(pname, lname, sizeof (pname));
11113431Scarlsonj if ((cp = strchr(pname, ':')) != NULL)
11123431Scarlsonj *cp = '\0';
11133431Scarlsonj
11143431Scarlsonj if ((pif = lookup_pif_by_name(pname, isv6)) != NULL)
11153431Scarlsonj hold_pif(pif);
11163431Scarlsonj else if ((pif = insert_pif(pname, isv6, error)) == NULL)
11173431Scarlsonj return (NULL);
11183431Scarlsonj
11193431Scarlsonj if (lookup_lif_by_name(lname, pif) != NULL) {
11203431Scarlsonj dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!",
11213431Scarlsonj lname);
11223431Scarlsonj release_pif(pif);
11233431Scarlsonj *error = DHCP_IPC_E_INVIF;
11243431Scarlsonj return (NULL);
11253431Scarlsonj }
11263431Scarlsonj
11273431Scarlsonj /* If LIF creation fails, then insert_lif discards our PIF hold */
11283431Scarlsonj return (insert_lif(pif, lname, error));
11293431Scarlsonj }
11303431Scarlsonj
11313431Scarlsonj /*
11323431Scarlsonj * set_lif_dhcp(): Set logical interface flags to show that it's managed
11333431Scarlsonj * by DHCP.
11343431Scarlsonj *
11353431Scarlsonj * input: dhcp_lif_t *: the logical interface
11363431Scarlsonj * output: int: set to DHCP_IPC_E_* if operation fails
11373431Scarlsonj */
11383431Scarlsonj
11393431Scarlsonj int
set_lif_dhcp(dhcp_lif_t * lif)114010785SPeter.Memishian@Sun.COM set_lif_dhcp(dhcp_lif_t *lif)
11413431Scarlsonj {
11423431Scarlsonj int fd;
11433431Scarlsonj int err;
11443431Scarlsonj struct lifreq lifr;
11458485SPeter.Memishian@Sun.COM dhcp_pif_t *pif = lif->lif_pif;
11463431Scarlsonj
11478485SPeter.Memishian@Sun.COM fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
11483431Scarlsonj
11493431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
11503431Scarlsonj
11513431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
11523431Scarlsonj err = errno;
11533431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
11543431Scarlsonj lif->lif_name);
11553431Scarlsonj return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT);
11563431Scarlsonj }
11573431Scarlsonj lif->lif_flags = lifr.lifr_flags;
11583431Scarlsonj
11593431Scarlsonj /*
11603431Scarlsonj * Check for conflicting sources of address control, and other
11613431Scarlsonj * unacceptable configurations.
11623431Scarlsonj */
11634823Sseb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
11644823Sseb IFF_VIRTUAL)) {
11653431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx",
11663431Scarlsonj lif->lif_name, lifr.lifr_flags);
11673431Scarlsonj return (DHCP_IPC_E_INVIF);
11683431Scarlsonj }
11693431Scarlsonj
11700Sstevel@tonic-gate /*
117110785SPeter.Memishian@Sun.COM * If IFF_DHCPRUNNING is already set on the interface and we're not
117210785SPeter.Memishian@Sun.COM * adopting it, the agent probably crashed and burned. Note it, but
117310785SPeter.Memishian@Sun.COM * don't let it stop the proceedings (we're pretty sure we're not
117410785SPeter.Memishian@Sun.COM * already running, since we were able to bind to our IPC port).
11750Sstevel@tonic-gate */
11763431Scarlsonj if (lifr.lifr_flags & IFF_DHCPRUNNING) {
117710785SPeter.Memishian@Sun.COM dhcpmsg(MSG_VERBOSE, "set_lif_dhcp: IFF_DHCPRUNNING already set"
117810785SPeter.Memishian@Sun.COM " on %s", lif->lif_name);
11793431Scarlsonj } else {
11808485SPeter.Memishian@Sun.COM /*
11818485SPeter.Memishian@Sun.COM * If the lif is on an interface under IPMP, IFF_NOFAILOVER
11828485SPeter.Memishian@Sun.COM * must be set or the kernel will prevent us from setting
11838485SPeter.Memishian@Sun.COM * IFF_DHCPRUNNING (since the subsequent IFF_UP would lead to
11848485SPeter.Memishian@Sun.COM * migration). We set IFF_DEPRECATED too since the kernel
11858485SPeter.Memishian@Sun.COM * will set it automatically when setting IFF_NOFAILOVER,
11868485SPeter.Memishian@Sun.COM * causing our lif_flags value to grow stale.
11878485SPeter.Memishian@Sun.COM */
11888485SPeter.Memishian@Sun.COM if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
11898485SPeter.Memishian@Sun.COM lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
11908485SPeter.Memishian@Sun.COM
11913431Scarlsonj lifr.lifr_flags |= IFF_DHCPRUNNING;
11923431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
11933431Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
11943431Scarlsonj lif->lif_name);
11953431Scarlsonj return (DHCP_IPC_E_INT);
11963431Scarlsonj }
11973431Scarlsonj lif->lif_flags = lifr.lifr_flags;
11980Sstevel@tonic-gate }
11993431Scarlsonj return (DHCP_IPC_SUCCESS);
12003431Scarlsonj }
12010Sstevel@tonic-gate
12023431Scarlsonj /*
12033431Scarlsonj * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
12043431Scarlsonj * managed by DHCP.
12053431Scarlsonj *
12063431Scarlsonj * input: dhcp_lif_t *: the logical interface
12073431Scarlsonj * output: none
12083431Scarlsonj */
12090Sstevel@tonic-gate
12103431Scarlsonj static void
clear_lif_dhcp(dhcp_lif_t * lif)12113431Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif)
12123431Scarlsonj {
12133431Scarlsonj int fd;
12143431Scarlsonj struct lifreq lifr;
12150Sstevel@tonic-gate
12163431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12173431Scarlsonj
12183431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12193431Scarlsonj
12203431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
12213431Scarlsonj return;
12220Sstevel@tonic-gate
12233431Scarlsonj if (!(lifr.lifr_flags & IFF_DHCPRUNNING))
12243431Scarlsonj return;
12250Sstevel@tonic-gate
12263431Scarlsonj lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING;
12273431Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
12283431Scarlsonj }
12290Sstevel@tonic-gate
12303431Scarlsonj /*
12313431Scarlsonj * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
12323431Scarlsonj * address will be going away. As the interface is
12333431Scarlsonj * going away, we don't care if there are errors.
12343431Scarlsonj *
12353431Scarlsonj * input: dhcp_lif_t *: the logical interface
12363431Scarlsonj * output: none
12373431Scarlsonj */
12380Sstevel@tonic-gate
12393431Scarlsonj void
set_lif_deprecated(dhcp_lif_t * lif)12403431Scarlsonj set_lif_deprecated(dhcp_lif_t *lif)
12413431Scarlsonj {
12423431Scarlsonj int fd;
12433431Scarlsonj struct lifreq lifr;
12440Sstevel@tonic-gate
12453431Scarlsonj if (lif->lif_flags & IFF_DEPRECATED)
12463431Scarlsonj return;
12470Sstevel@tonic-gate
12483431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12493431Scarlsonj
12503431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12510Sstevel@tonic-gate
12523431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
12533431Scarlsonj return;
12543431Scarlsonj
12553431Scarlsonj if (lifr.lifr_flags & IFF_DEPRECATED)
12563431Scarlsonj return;
12570Sstevel@tonic-gate
12583431Scarlsonj lifr.lifr_flags |= IFF_DEPRECATED;
12593431Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
12603431Scarlsonj lif->lif_flags = lifr.lifr_flags;
12613431Scarlsonj }
12623431Scarlsonj
12633431Scarlsonj /*
12643431Scarlsonj * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
12653431Scarlsonj * address will not be going away. This happens if we
12663431Scarlsonj * get a renewal after preferred lifetime but before
12673431Scarlsonj * the valid lifetime.
12683431Scarlsonj *
12693431Scarlsonj * input: dhcp_lif_t *: the logical interface
12703431Scarlsonj * output: boolean_t: B_TRUE on success.
12713431Scarlsonj */
12720Sstevel@tonic-gate
12733431Scarlsonj boolean_t
clear_lif_deprecated(dhcp_lif_t * lif)12743431Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif)
12753431Scarlsonj {
12763431Scarlsonj int fd;
12773431Scarlsonj struct lifreq lifr;
12783431Scarlsonj
12793431Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12803431Scarlsonj
12813431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12823431Scarlsonj
12833431Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
12843431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
12853431Scarlsonj lif->lif_name);
12863431Scarlsonj return (B_FALSE);
12870Sstevel@tonic-gate }
12880Sstevel@tonic-gate
12890Sstevel@tonic-gate /*
12903431Scarlsonj * Check for conflicting sources of address control, and other
12913431Scarlsonj * unacceptable configurations.
12920Sstevel@tonic-gate */
12934823Sseb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
12944823Sseb IFF_VIRTUAL)) {
12953431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags "
12963431Scarlsonj "are %llx", lif->lif_name, lifr.lifr_flags);
12973431Scarlsonj return (B_FALSE);
12980Sstevel@tonic-gate }
12990Sstevel@tonic-gate
13008485SPeter.Memishian@Sun.COM /*
13018485SPeter.Memishian@Sun.COM * Don't try to clear IFF_DEPRECATED if this is a test address,
13028485SPeter.Memishian@Sun.COM * since IPMP's use of IFF_DEPRECATED is not compatible with ours.
13038485SPeter.Memishian@Sun.COM */
13048485SPeter.Memishian@Sun.COM if (lifr.lifr_flags & IFF_NOFAILOVER)
13058485SPeter.Memishian@Sun.COM return (B_TRUE);
13068485SPeter.Memishian@Sun.COM
13073431Scarlsonj if (!(lifr.lifr_flags & IFF_DEPRECATED))
13083431Scarlsonj return (B_TRUE);
13090Sstevel@tonic-gate
13103431Scarlsonj lifr.lifr_flags &= ~IFF_DEPRECATED;
13113431Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
13123431Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
13133431Scarlsonj lif->lif_name);
13143431Scarlsonj return (B_FALSE);
13153431Scarlsonj } else {
13163431Scarlsonj lif->lif_flags = lifr.lifr_flags;
13173431Scarlsonj return (B_TRUE);
13180Sstevel@tonic-gate }
13190Sstevel@tonic-gate }
13200Sstevel@tonic-gate
13210Sstevel@tonic-gate /*
13223431Scarlsonj * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
13230Sstevel@tonic-gate *
13243431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
13255381Smeem * in_addr_t: the address the socket will be bound to (in hbo)
13268485SPeter.Memishian@Sun.COM * boolean_t: B_TRUE if the address should be brought up (if needed)
13273431Scarlsonj * output: boolean_t: B_TRUE if the socket was opened successfully.
13280Sstevel@tonic-gate */
13290Sstevel@tonic-gate
13303431Scarlsonj boolean_t
open_ip_lif(dhcp_lif_t * lif,in_addr_t addr_hbo,boolean_t bringup)13318485SPeter.Memishian@Sun.COM open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo, boolean_t bringup)
13320Sstevel@tonic-gate {
13335381Smeem const char *errmsg;
13345381Smeem struct lifreq lifr;
13355381Smeem int on = 1;
13365455Smeem uchar_t ttl = 255;
13378485SPeter.Memishian@Sun.COM uint32_t ifindex;
13388485SPeter.Memishian@Sun.COM dhcp_pif_t *pif = lif->lif_pif;
13395381Smeem
13403431Scarlsonj if (lif->lif_sock_ip_fd != -1) {
13413431Scarlsonj dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
13423431Scarlsonj lif->lif_name);
13433431Scarlsonj return (B_FALSE);
13440Sstevel@tonic-gate }
13450Sstevel@tonic-gate
13463431Scarlsonj lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
13473431Scarlsonj if (lif->lif_sock_ip_fd == -1) {
13485381Smeem errmsg = "cannot create v4 socket";
13495381Smeem goto failure;
13505381Smeem }
13515381Smeem
13525381Smeem if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) {
13535381Smeem errmsg = "cannot bind v4 socket";
13545381Smeem goto failure;
13555381Smeem }
13565381Smeem
13575381Smeem /*
13585381Smeem * If we bound to INADDR_ANY, we have no IFF_UP source address to use.
13595381Smeem * Thus, enable IP_UNSPEC_SRC so that we can send packets with an
13605381Smeem * unspecified (0.0.0.0) address. Also, enable IP_DHCPINIT_IF so that
13615381Smeem * the IP module will accept unicast DHCP traffic regardless of the IP
13625381Smeem * address it's sent to. (We'll then figure out which packets are
13635381Smeem * ours based on the xid.)
13645381Smeem */
13655381Smeem if (addr_hbo == INADDR_ANY) {
13665381Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC,
13675381Smeem &on, sizeof (int)) == -1) {
13685381Smeem errmsg = "cannot set IP_UNSPEC_SRC";
13695381Smeem goto failure;
13705381Smeem }
13715381Smeem
13725381Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF,
13738485SPeter.Memishian@Sun.COM &pif->pif_index, sizeof (int)) == -1) {
13745381Smeem errmsg = "cannot set IP_DHCPINIT_IF";
13755381Smeem goto failure;
13765381Smeem }
13775381Smeem }
13785381Smeem
13795455Smeem /*
13805455Smeem * Unfortunately, some hardware (such as the Linksys WRT54GC)
13815455Smeem * decrements the TTL *prior* to accepting DHCP traffic destined
13825455Smeem * for it. To workaround this, tell IP to use a TTL of 255 for
13835455Smeem * broadcast packets sent from this socket.
13845455Smeem */
13855455Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BROADCAST_TTL, &ttl,
13865455Smeem sizeof (uchar_t)) == -1) {
13875455Smeem errmsg = "cannot set IP_BROADCAST_TTL";
13885455Smeem goto failure;
13895455Smeem }
13905455Smeem
13918485SPeter.Memishian@Sun.COM ifindex = pif->pif_under_ipmp ? pif->pif_grindex : pif->pif_index;
13928485SPeter.Memishian@Sun.COM if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, &ifindex,
13938485SPeter.Memishian@Sun.COM sizeof (int)) == -1) {
13945381Smeem errmsg = "cannot set IP_BOUND_IF";
13955381Smeem goto failure;
13960Sstevel@tonic-gate }
13970Sstevel@tonic-gate
13985381Smeem (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
13995381Smeem if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
14005381Smeem errmsg = "cannot get interface flags";
14015381Smeem goto failure;
14023431Scarlsonj }
14030Sstevel@tonic-gate
14048485SPeter.Memishian@Sun.COM /*
14058485SPeter.Memishian@Sun.COM * If the lif is part of an interface under IPMP, IFF_NOFAILOVER must
14068485SPeter.Memishian@Sun.COM * be set or the kernel will prevent us from setting IFF_DHCPRUNNING
14078485SPeter.Memishian@Sun.COM * (since the subsequent IFF_UP would lead to migration). We set
14088485SPeter.Memishian@Sun.COM * IFF_DEPRECATED too since the kernel will set it automatically when
14098485SPeter.Memishian@Sun.COM * setting IFF_NOFAILOVER, causing our lif_flags value to grow stale.
14108485SPeter.Memishian@Sun.COM */
14118485SPeter.Memishian@Sun.COM if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER)) {
14128485SPeter.Memishian@Sun.COM lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
14138485SPeter.Memishian@Sun.COM if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
14148485SPeter.Memishian@Sun.COM errmsg = "cannot set IFF_NOFAILOVER";
14158485SPeter.Memishian@Sun.COM goto failure;
14168485SPeter.Memishian@Sun.COM }
14178485SPeter.Memishian@Sun.COM }
14188485SPeter.Memishian@Sun.COM lif->lif_flags = lifr.lifr_flags;
14198485SPeter.Memishian@Sun.COM
14208485SPeter.Memishian@Sun.COM /*
14218485SPeter.Memishian@Sun.COM * If this is initial bringup, make sure the address we're acquiring a
14228485SPeter.Memishian@Sun.COM * lease on is IFF_UP.
14238485SPeter.Memishian@Sun.COM */
14248485SPeter.Memishian@Sun.COM if (bringup && !(lifr.lifr_flags & IFF_UP)) {
14255381Smeem /*
14265381Smeem * Start from a clean slate.
14275381Smeem */
14285381Smeem canonize_lif(lif, B_FALSE);
14295381Smeem
14305381Smeem lifr.lifr_flags |= IFF_UP;
14315381Smeem if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
14325381Smeem errmsg = "cannot bring up";
14335381Smeem goto failure;
14345381Smeem }
14355439Smeem lif->lif_flags = lifr.lifr_flags;
14365381Smeem
14375381Smeem /*
14385381Smeem * When bringing 0.0.0.0 IFF_UP, the kernel changes the
14395381Smeem * netmask to 255.0.0.0, so re-fetch our expected netmask.
14405381Smeem */
14415381Smeem if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) {
14425381Smeem errmsg = "cannot get netmask";
14435381Smeem goto failure;
14445381Smeem }
14455381Smeem
14465381Smeem lif->lif_netmask =
14475381Smeem ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
14480Sstevel@tonic-gate }
14495381Smeem
14508485SPeter.Memishian@Sun.COM /*
14518485SPeter.Memishian@Sun.COM * Usually, bringing up the address we're acquiring a lease on is
14528485SPeter.Memishian@Sun.COM * sufficient to allow packets to be sent and received via the
14538485SPeter.Memishian@Sun.COM * IP_BOUND_IF we did earlier. However, if we're acquiring a lease on
14548485SPeter.Memishian@Sun.COM * an underlying IPMP interface, the group interface will be used for
14558485SPeter.Memishian@Sun.COM * sending and receiving IP packets via IP_BOUND_IF. Thus, ensure at
14568485SPeter.Memishian@Sun.COM * least one address on the group interface is IFF_UP.
14578485SPeter.Memishian@Sun.COM */
14588485SPeter.Memishian@Sun.COM if (bringup && pif->pif_under_ipmp) {
14598485SPeter.Memishian@Sun.COM (void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
14608485SPeter.Memishian@Sun.COM if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
14618485SPeter.Memishian@Sun.COM errmsg = "cannot get IPMP group interface flags";
14628485SPeter.Memishian@Sun.COM goto failure;
14638485SPeter.Memishian@Sun.COM }
14648485SPeter.Memishian@Sun.COM
14658485SPeter.Memishian@Sun.COM if (!(lifr.lifr_flags & IFF_UP)) {
14668485SPeter.Memishian@Sun.COM lifr.lifr_flags |= IFF_UP;
14678485SPeter.Memishian@Sun.COM if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
14688485SPeter.Memishian@Sun.COM errmsg = "cannot bring up IPMP group interface";
14698485SPeter.Memishian@Sun.COM goto failure;
14708485SPeter.Memishian@Sun.COM }
14718485SPeter.Memishian@Sun.COM }
14728485SPeter.Memishian@Sun.COM }
14738485SPeter.Memishian@Sun.COM
14745381Smeem lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
14755381Smeem dhcp_packet_lif, lif);
14765381Smeem if (lif->lif_packet_id == -1) {
14775381Smeem errmsg = "cannot register to receive DHCP packets";
14785381Smeem goto failure;
14795381Smeem }
14805381Smeem
14810Sstevel@tonic-gate return (B_TRUE);
14825381Smeem failure:
14835381Smeem dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg);
14845381Smeem close_ip_lif(lif);
14855381Smeem return (B_FALSE);
14860Sstevel@tonic-gate }
14870Sstevel@tonic-gate
14880Sstevel@tonic-gate /*
14893431Scarlsonj * close_ip_lif(): close an IP socket for I/O on a given LIF.
14900Sstevel@tonic-gate *
14913431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
14923431Scarlsonj * output: none
14930Sstevel@tonic-gate */
14940Sstevel@tonic-gate
14953431Scarlsonj void
close_ip_lif(dhcp_lif_t * lif)14963431Scarlsonj close_ip_lif(dhcp_lif_t *lif)
14970Sstevel@tonic-gate {
14985381Smeem if (lif->lif_packet_id != -1) {
14995381Smeem (void) iu_unregister_event(eh, lif->lif_packet_id, NULL);
15005381Smeem lif->lif_packet_id = -1;
15013431Scarlsonj }
15023431Scarlsonj if (lif->lif_sock_ip_fd != -1) {
15033431Scarlsonj (void) close(lif->lif_sock_ip_fd);
15043431Scarlsonj lif->lif_sock_ip_fd = -1;
15053431Scarlsonj }
15063431Scarlsonj }
15070Sstevel@tonic-gate
15083431Scarlsonj /*
15093431Scarlsonj * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
15103431Scarlsonj * address or some other conflict. This is used in
15113431Scarlsonj * send_declines() to report failure back to the server.
15123431Scarlsonj *
15133431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
15143431Scarlsonj * const char *: text string explaining why the address is declined
15153431Scarlsonj * output: none
15163431Scarlsonj */
15170Sstevel@tonic-gate
15183431Scarlsonj void
lif_mark_decline(dhcp_lif_t * lif,const char * reason)15193431Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason)
15203431Scarlsonj {
15213431Scarlsonj if (lif->lif_declined == NULL) {
15223431Scarlsonj dhcp_lease_t *dlp;
15230Sstevel@tonic-gate
15243431Scarlsonj lif->lif_declined = reason;
15253431Scarlsonj if ((dlp = lif->lif_lease) != NULL)
15263431Scarlsonj dlp->dl_smach->dsm_lif_down++;
15270Sstevel@tonic-gate }
15280Sstevel@tonic-gate }
15290Sstevel@tonic-gate
15300Sstevel@tonic-gate /*
15313431Scarlsonj * schedule_lif_timer(): schedules the LIF-related timer
15320Sstevel@tonic-gate *
15333431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
15343431Scarlsonj * dhcp_timer_t *: the timer to schedule
15353431Scarlsonj * iu_tq_callback_t *: the callback to call upon firing
15363431Scarlsonj * output: boolean_t: B_TRUE if the timer was scheduled successfully
15370Sstevel@tonic-gate */
15380Sstevel@tonic-gate
15393431Scarlsonj boolean_t
schedule_lif_timer(dhcp_lif_t * lif,dhcp_timer_t * dt,iu_tq_callback_t * expire)15403431Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire)
15410Sstevel@tonic-gate {
15420Sstevel@tonic-gate /*
15433431Scarlsonj * If there's a timer running, cancel it and release its lease
15443431Scarlsonj * reference.
15450Sstevel@tonic-gate */
15463431Scarlsonj if (dt->dt_id != -1) {
15473431Scarlsonj if (!cancel_timer(dt))
15483431Scarlsonj return (B_FALSE);
15493431Scarlsonj release_lif(lif);
15503431Scarlsonj }
15510Sstevel@tonic-gate
15523431Scarlsonj if (schedule_timer(dt, expire, lif)) {
15533431Scarlsonj hold_lif(lif);
15543431Scarlsonj return (B_TRUE);
15553431Scarlsonj } else {
15563431Scarlsonj dhcpmsg(MSG_WARNING,
15573431Scarlsonj "schedule_lif_timer: cannot schedule timer");
15583431Scarlsonj return (B_FALSE);
15593431Scarlsonj }
15600Sstevel@tonic-gate }
15610Sstevel@tonic-gate
15620Sstevel@tonic-gate /*
15633431Scarlsonj * cancel_lif_timer(): cancels a LIF-related timer
15640Sstevel@tonic-gate *
15653431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
15663431Scarlsonj * dhcp_timer_t *: the timer to cancel
15673431Scarlsonj * output: none
15680Sstevel@tonic-gate */
15690Sstevel@tonic-gate
15703431Scarlsonj static void
cancel_lif_timer(dhcp_lif_t * lif,dhcp_timer_t * dt)15713431Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt)
15720Sstevel@tonic-gate {
15733431Scarlsonj if (dt->dt_id == -1)
15743431Scarlsonj return;
15753431Scarlsonj if (cancel_timer(dt)) {
15763431Scarlsonj dhcpmsg(MSG_DEBUG2,
15773431Scarlsonj "cancel_lif_timer: canceled expiry timer on %s",
15783431Scarlsonj lif->lif_name);
15793431Scarlsonj release_lif(lif);
15803431Scarlsonj } else {
15813431Scarlsonj dhcpmsg(MSG_WARNING,
15823431Scarlsonj "cancel_lif_timer: cannot cancel timer on %s",
15833431Scarlsonj lif->lif_name);
15840Sstevel@tonic-gate }
15850Sstevel@tonic-gate }
15860Sstevel@tonic-gate
15870Sstevel@tonic-gate /*
15883431Scarlsonj * cancel_lif_timers(): cancels the LIF-related timers
15890Sstevel@tonic-gate *
15903431Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
15913431Scarlsonj * output: none
15920Sstevel@tonic-gate */
15930Sstevel@tonic-gate
15940Sstevel@tonic-gate void
cancel_lif_timers(dhcp_lif_t * lif)15953431Scarlsonj cancel_lif_timers(dhcp_lif_t *lif)
15960Sstevel@tonic-gate {
15973431Scarlsonj cancel_lif_timer(lif, &lif->lif_preferred);
15983431Scarlsonj cancel_lif_timer(lif, &lif->lif_expire);
15990Sstevel@tonic-gate }
16000Sstevel@tonic-gate
16010Sstevel@tonic-gate /*
16023431Scarlsonj * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
16033431Scarlsonj * file descriptors (v4_sock_fd and v6_sock_fd).
16040Sstevel@tonic-gate *
16053431Scarlsonj * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
16063431Scarlsonj * output: none
16070Sstevel@tonic-gate */
16080Sstevel@tonic-gate
16093431Scarlsonj uint_t
get_max_mtu(boolean_t isv6)16103431Scarlsonj get_max_mtu(boolean_t isv6)
16110Sstevel@tonic-gate {
16123431Scarlsonj uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu;
16133431Scarlsonj
16143431Scarlsonj if (*mtup == 0) {
16153431Scarlsonj dhcp_pif_t *pif;
16163431Scarlsonj dhcp_lif_t *lif;
16173431Scarlsonj struct lifreq lifr;
16183431Scarlsonj
16193431Scarlsonj /* Set an arbitrary lower bound */
16203431Scarlsonj *mtup = 1024;
16213431Scarlsonj pif = isv6 ? v6root : v4root;
16223431Scarlsonj for (; pif != NULL; pif = pif->pif_next) {
16233431Scarlsonj for (lif = pif->pif_lifs; lif != NULL;
16243431Scarlsonj lif = lif->lif_next) {
16253431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name,
16263431Scarlsonj LIFNAMSIZ);
16273431Scarlsonj if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) !=
16283431Scarlsonj -1 && lifr.lifr_mtu > *mtup) {
16293431Scarlsonj *mtup = lifr.lifr_mtu;
16303431Scarlsonj }
16313431Scarlsonj }
16323431Scarlsonj }
16330Sstevel@tonic-gate }
16343431Scarlsonj return (*mtup);
16350Sstevel@tonic-gate }
16360Sstevel@tonic-gate
16370Sstevel@tonic-gate /*
16383431Scarlsonj * expired_lif_state(): summarize the state of expired LIFs on a given state
16393431Scarlsonj * machine.
16400Sstevel@tonic-gate *
16413431Scarlsonj * input: dhcp_smach_t *: the state machine to scan
16423431Scarlsonj * output: dhcp_expire_t: overall state
16430Sstevel@tonic-gate */
16440Sstevel@tonic-gate
16453431Scarlsonj dhcp_expire_t
expired_lif_state(dhcp_smach_t * dsmp)16463431Scarlsonj expired_lif_state(dhcp_smach_t *dsmp)
16470Sstevel@tonic-gate {
16483431Scarlsonj dhcp_lease_t *dlp;
16493431Scarlsonj dhcp_lif_t *lif;
16503431Scarlsonj uint_t nlifs;
16513431Scarlsonj uint_t numlifs;
16523431Scarlsonj uint_t numexp;
16530Sstevel@tonic-gate
16543431Scarlsonj numlifs = numexp = 0;
16553431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
16563431Scarlsonj lif = dlp->dl_lifs;
16573431Scarlsonj nlifs = dlp->dl_nlifs;
16583431Scarlsonj numlifs += nlifs;
16593431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
16603431Scarlsonj if (lif->lif_expired)
16613431Scarlsonj numexp++;
16623431Scarlsonj }
16630Sstevel@tonic-gate }
16643431Scarlsonj if (numlifs == 0)
16653431Scarlsonj return (DHCP_EXP_NOLIFS);
16663431Scarlsonj else if (numexp == 0)
16673431Scarlsonj return (DHCP_EXP_NOEXP);
16683431Scarlsonj else if (numlifs == numexp)
16693431Scarlsonj return (DHCP_EXP_ALLEXP);
16703431Scarlsonj else
16713431Scarlsonj return (DHCP_EXP_SOMEEXP);
16720Sstevel@tonic-gate }
16730Sstevel@tonic-gate
16740Sstevel@tonic-gate /*
16753431Scarlsonj * find_expired_lif(): find the first expired LIF on a given state machine
16760Sstevel@tonic-gate *
16773431Scarlsonj * input: dhcp_smach_t *: the state machine to scan
16783431Scarlsonj * output: dhcp_lif_t *: the first expired LIF, or NULL if none.
16790Sstevel@tonic-gate */
16800Sstevel@tonic-gate
16813431Scarlsonj dhcp_lif_t *
find_expired_lif(dhcp_smach_t * dsmp)16823431Scarlsonj find_expired_lif(dhcp_smach_t *dsmp)
16830Sstevel@tonic-gate {
16843431Scarlsonj dhcp_lease_t *dlp;
16853431Scarlsonj dhcp_lif_t *lif;
16863431Scarlsonj uint_t nlifs;
16873431Scarlsonj
16883431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
16893431Scarlsonj lif = dlp->dl_lifs;
16903431Scarlsonj nlifs = dlp->dl_nlifs;
16913431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
16923431Scarlsonj if (lif->lif_expired)
16933431Scarlsonj return (lif);
16943431Scarlsonj }
16953431Scarlsonj }
16963431Scarlsonj return (NULL);
16973431Scarlsonj }
16983431Scarlsonj
16993431Scarlsonj /*
17003431Scarlsonj * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used
17013431Scarlsonj * only for DHCPv6.
17023431Scarlsonj *
17033431Scarlsonj * input: none
17043431Scarlsonj * output: none
17053431Scarlsonj */
17063431Scarlsonj
17073431Scarlsonj void
remove_v6_strays(void)17083431Scarlsonj remove_v6_strays(void)
17093431Scarlsonj {
17103431Scarlsonj struct lifnum lifn;
17113431Scarlsonj struct lifconf lifc;
17123431Scarlsonj struct lifreq *lifrp, *lifrmax;
17133431Scarlsonj uint_t numifs;
17143431Scarlsonj uint64_t flags;
17150Sstevel@tonic-gate
17160Sstevel@tonic-gate /*
17173431Scarlsonj * Get the approximate number of interfaces in the system. It's only
17183431Scarlsonj * approximate because the system is dynamic -- interfaces may be
17193431Scarlsonj * plumbed or unplumbed at any time. This is also the reason for the
17203431Scarlsonj * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
17210Sstevel@tonic-gate */
17223431Scarlsonj (void) memset(&lifn, 0, sizeof (lifn));
17233431Scarlsonj lifn.lifn_family = AF_INET6;
17243431Scarlsonj lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
17253431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) {
17263431Scarlsonj dhcpmsg(MSG_ERR,
17273431Scarlsonj "remove_v6_strays: cannot read number of interfaces");
17283431Scarlsonj numifs = 10;
17293431Scarlsonj } else {
17303431Scarlsonj numifs = lifn.lifn_count + 10;
17310Sstevel@tonic-gate }
17320Sstevel@tonic-gate
17330Sstevel@tonic-gate /*
17343431Scarlsonj * Get the interface information. We do this in a loop so that we can
17353431Scarlsonj * recover from EINVAL from the kernel -- delivered when the buffer is
17363431Scarlsonj * too small.
17370Sstevel@tonic-gate */
17383431Scarlsonj (void) memset(&lifc, 0, sizeof (lifc));
17393431Scarlsonj lifc.lifc_family = AF_INET6;
17403431Scarlsonj lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
17413431Scarlsonj for (;;) {
17423431Scarlsonj lifc.lifc_len = numifs * sizeof (*lifrp);
17433431Scarlsonj lifrp = realloc(lifc.lifc_buf, lifc.lifc_len);
17443431Scarlsonj if (lifrp == NULL) {
17453431Scarlsonj dhcpmsg(MSG_ERR,
17463431Scarlsonj "remove_v6_strays: cannot allocate memory");
17473431Scarlsonj free(lifc.lifc_buf);
17483431Scarlsonj return;
17490Sstevel@tonic-gate }
17503431Scarlsonj lifc.lifc_buf = (caddr_t)lifrp;
17513431Scarlsonj errno = 0;
17523431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 &&
17533431Scarlsonj lifc.lifc_len < numifs * sizeof (*lifrp))
17540Sstevel@tonic-gate break;
17553431Scarlsonj if (errno == 0 || errno == EINVAL) {
17563431Scarlsonj numifs <<= 1;
17573431Scarlsonj } else {
17583431Scarlsonj dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF");
17593431Scarlsonj free(lifc.lifc_buf);
17603431Scarlsonj return;
17610Sstevel@tonic-gate }
17620Sstevel@tonic-gate }
17630Sstevel@tonic-gate
17643431Scarlsonj lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp);
17653431Scarlsonj for (; lifrp < lifrmax; lifrp++) {
17660Sstevel@tonic-gate /*
17673431Scarlsonj * Get the interface flags; we're interested in the DHCP ones.
17680Sstevel@tonic-gate */
17693431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1)
17703431Scarlsonj continue;
17713431Scarlsonj flags = lifrp->lifr_flags;
17723431Scarlsonj if (!(flags & IFF_DHCPRUNNING))
17733431Scarlsonj continue;
17740Sstevel@tonic-gate /*
17753431Scarlsonj * If the interface has a link-local address, then we don't
17763431Scarlsonj * control it. Just remove the flag.
17770Sstevel@tonic-gate */
17783431Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1)
17793431Scarlsonj continue;
17803431Scarlsonj if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp->
17813431Scarlsonj lifr_addr)->sin6_addr)) {
17823431Scarlsonj lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING;
17833431Scarlsonj (void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp);
17843431Scarlsonj continue;
17850Sstevel@tonic-gate }
17860Sstevel@tonic-gate /*
17873431Scarlsonj * All others are (or were) under our control. Clean up by
17883431Scarlsonj * removing them.
17890Sstevel@tonic-gate */
17903431Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) {
17913431Scarlsonj dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s",
17923431Scarlsonj lifrp->lifr_name);
17933431Scarlsonj } else if (errno != ENXIO) {
17943431Scarlsonj dhcpmsg(MSG_ERR,
17953431Scarlsonj "remove_v6_strays: SIOCLIFREMOVEIF %s",
17963431Scarlsonj lifrp->lifr_name);
17970Sstevel@tonic-gate }
17980Sstevel@tonic-gate }
17993431Scarlsonj free(lifc.lifc_buf);
18000Sstevel@tonic-gate }
1801