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