Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 /*
     30  * IPMP query interfaces (PSARC/2002/615).
     31  */
     32 
     33 #include <assert.h>
     34 #include <errno.h>
     35 #include <string.h>
     36 #include <stdlib.h>
     37 #include <unistd.h>
     38 #include <sys/types.h>
     39 
     40 #include "ipmp_impl.h"
     41 #include "ipmp_mpathd.h"
     42 #include "ipmp_query_impl.h"
     43 
     44 #define	IPMP_REQTIMEOUT	5	/* seconds */
     45 
     46 static ipmp_ifinfo_t	*ipmp_ifinfo_clone(ipmp_ifinfo_t *);
     47 static ipmp_grouplist_t	*ipmp_grouplist_clone(ipmp_grouplist_t *);
     48 static ipmp_groupinfo_t	*ipmp_groupinfo_clone(ipmp_groupinfo_t *);
     49 static ipmp_groupinfo_t *ipmp_snap_getgroupinfo(ipmp_snap_t *, const char *);
     50 static ipmp_ifinfo_t	*ipmp_snap_getifinfo(ipmp_snap_t *, const char *);
     51 static int		ipmp_snap_take(ipmp_state_t *, ipmp_snap_t **);
     52 static boolean_t	ipmp_checktlv(ipmp_infotype_t, size_t, void *);
     53 static int		ipmp_querydone(ipmp_state_t *, int);
     54 
     55 /*
     56  * Using `statep', send a query request for `type' to in.mpathd, and if
     57  * necessary wait until at least `endtp' for a response.  Returns an IPMP
     58  * error code.  If successful, the caller may then read additional query
     59  * information through ipmp_readinfo(), and must eventually call
     60  * ipmp_querydone() to complete the query operation.  Only one query may be
     61  * outstanding on a given `statep' at a time.
     62  */
     63 static int
     64 ipmp_sendquery(ipmp_state_t *statep, ipmp_infotype_t type, const char *name,
     65     struct timeval *endtp)
     66 {
     67 	mi_query_t	query;
     68 	mi_result_t	result;
     69 	int		retval;
     70 
     71 	query.miq_command = MI_QUERY;
     72 	query.miq_inforeq = type;
     73 
     74 	switch (type) {
     75 	case IPMP_GROUPINFO:
     76 		(void) strlcpy(query.miq_grname, name, LIFGRNAMSIZ);
     77 		break;
     78 
     79 	case IPMP_IFINFO:
     80 		(void) strlcpy(query.miq_ifname, name, LIFNAMSIZ);
     81 		break;
     82 
     83 	case IPMP_GROUPLIST:
     84 	case IPMP_SNAP:
     85 		break;
     86 
     87 	default:
     88 		assert(0);
     89 	}
     90 
     91 	if (gettimeofday(endtp, NULL) == -1)
     92 		return (IPMP_FAILURE);
     93 
     94 	endtp->tv_sec += IPMP_REQTIMEOUT;
     95 
     96 	assert(statep->st_fd == -1);
     97 	retval = ipmp_connect(&statep->st_fd);
     98 	if (retval != IPMP_SUCCESS)
     99 		return (retval);
    100 
    101 	retval = ipmp_write(statep->st_fd, &query, sizeof (query));
    102 	if (retval != IPMP_SUCCESS)
    103 		return (ipmp_querydone(statep, retval));
    104 
    105 	retval = ipmp_read(statep->st_fd, &result, sizeof (result), endtp);
    106 	if (retval != IPMP_SUCCESS)
    107 		return (ipmp_querydone(statep, retval));
    108 
    109 	if (result.me_mpathd_error != IPMP_SUCCESS)
    110 		return (ipmp_querydone(statep, result.me_mpathd_error));
    111 
    112 	return (IPMP_SUCCESS);
    113 }
    114 
    115 /*
    116  * Using `statep', read a query response of type `infotype' into a dynamically
    117  * allocated buffer pointed to by `*infop', before the current time becomes
    118  * `endtp'.  Returns an IPMP error code.
    119  */
    120 static int
    121 ipmp_readinfo(ipmp_state_t *statep, ipmp_infotype_t infotype, void **infop,
    122     const struct timeval *endtp)
    123 {
    124 	int		retval;
    125 	size_t		len;
    126 	ipmp_infotype_t	type;
    127 
    128 	retval = ipmp_readtlv(statep->st_fd, &type, &len, infop, endtp);
    129 	if (retval != IPMP_SUCCESS)
    130 		return (retval);
    131 
    132 	if (type != infotype || !ipmp_checktlv(type, len, *infop)) {
    133 		free(*infop);
    134 		return (IPMP_EPROTO);
    135 	}
    136 
    137 	return (IPMP_SUCCESS);
    138 }
    139 
    140 /*
    141  * Complete the query operation started in ipmp_sendquery().  The interface is
    142  * designed to be easy to use in the `return' statement of a function, and
    143  * thus returns the passed in `retval' and preserves `errno'.
    144  */
    145 static int
    146 ipmp_querydone(ipmp_state_t *statep, int retval)
    147 {
    148 	int error = errno;
    149 
    150 	(void) close(statep->st_fd);
    151 	statep->st_fd = -1;
    152 	errno = error;
    153 	return (retval);
    154 }
    155 
    156 /*
    157  * Using `handle', get the group list and store the results in a dynamically
    158  * allocated buffer pointed to by `*grlistpp'.  Returns an IPMP error code.
    159  */
    160 int
    161 ipmp_getgrouplist(ipmp_handle_t handle, ipmp_grouplist_t **grlistpp)
    162 {
    163 	ipmp_state_t	*statep = handle;
    164 	struct timeval	end;
    165 	int		retval;
    166 
    167 	if (statep->st_snap != NULL) {
    168 		*grlistpp = ipmp_grouplist_clone(statep->st_snap->sn_grlistp);
    169 		return (*grlistpp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM);
    170 	}
    171 
    172 	retval = ipmp_sendquery(statep, IPMP_GROUPLIST, NULL, &end);
    173 	if (retval != IPMP_SUCCESS)
    174 		return (retval);
    175 
    176 	retval = ipmp_readinfo(statep, IPMP_GROUPLIST, (void **)grlistpp, &end);
    177 	return (ipmp_querydone(statep, retval));
    178 }
    179 
    180 /*
    181  * Free the group list pointed to by `grlistp'.
    182  */
    183 void
    184 ipmp_freegrouplist(ipmp_grouplist_t *grlistp)
    185 {
    186 	free(grlistp);
    187 }
    188 
    189 /*
    190  * Using `handle', get the group information associated with group `name' and
    191  * store the results in a dynamically allocated buffer pointed to by
    192  * `*grinfopp'.  Returns an IPMP error code.
    193  */
    194 int
    195 ipmp_getgroupinfo(ipmp_handle_t handle, const char *name,
    196     ipmp_groupinfo_t **grinfopp)
    197 {
    198 	ipmp_state_t	*statep = handle;
    199 	ipmp_iflist_t	*iflistp;
    200 	int		retval;
    201 	struct timeval	end;
    202 	ipmp_groupinfo_t *grinfop;
    203 
    204 	if (statep->st_snap != NULL) {
    205 		grinfop = ipmp_snap_getgroupinfo(statep->st_snap, name);
    206 		if (grinfop == NULL)
    207 			return (IPMP_EUNKGROUP);
    208 
    209 		*grinfopp = ipmp_groupinfo_clone(grinfop);
    210 		return (*grinfopp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM);
    211 	}
    212 
    213 	retval = ipmp_sendquery(statep, IPMP_GROUPINFO, name, &end);
    214 	if (retval != IPMP_SUCCESS)
    215 		return (retval);
    216 
    217 	retval = ipmp_readinfo(statep, IPMP_GROUPINFO, (void **)grinfopp, &end);
    218 	if (retval != IPMP_SUCCESS)
    219 		return (ipmp_querydone(statep, retval));
    220 
    221 	retval = ipmp_readinfo(statep, IPMP_IFLIST, (void **)&iflistp, &end);
    222 	if (retval != IPMP_SUCCESS)
    223 		free(*grinfopp);
    224 	else
    225 		(*grinfopp)->gr_iflistp = iflistp;
    226 
    227 	return (ipmp_querydone(statep, retval));
    228 }
    229 
    230 /*
    231  * Free the group information pointed to by `grinfop'.
    232  */
    233 void
    234 ipmp_freegroupinfo(ipmp_groupinfo_t *grinfop)
    235 {
    236 	free(grinfop->gr_iflistp);
    237 	free(grinfop);
    238 }
    239 
    240 /*
    241  * Using `handle', get the interface information associated with interface
    242  * `name' and store the results in a dynamically allocated buffer pointed to
    243  * by `*ifinfopp'.  Returns an IPMP error code.
    244  */
    245 int
    246 ipmp_getifinfo(ipmp_handle_t handle, const char *name, ipmp_ifinfo_t **ifinfopp)
    247 {
    248 	ipmp_state_t	*statep = handle;
    249 	ipmp_ifinfo_t	*ifinfop;
    250 	int		retval;
    251 	struct timeval	end;
    252 
    253 	if (statep->st_snap != NULL) {
    254 		ifinfop = ipmp_snap_getifinfo(statep->st_snap, name);
    255 		if (ifinfop == NULL)
    256 			return (IPMP_EUNKIF);
    257 
    258 		*ifinfopp = ipmp_ifinfo_clone(ifinfop);
    259 		return (*ifinfopp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM);
    260 	}
    261 
    262 	retval = ipmp_sendquery(statep, IPMP_IFINFO, name, &end);
    263 	if (retval != IPMP_SUCCESS)
    264 		return (retval);
    265 
    266 	retval = ipmp_readinfo(statep, IPMP_IFINFO, (void **)ifinfopp, &end);
    267 	return (ipmp_querydone(statep, retval));
    268 }
    269 
    270 /*
    271  * Free the interface information pointed to by `ifinfop'.
    272  */
    273 void
    274 ipmp_freeifinfo(ipmp_ifinfo_t *ifinfop)
    275 {
    276 	free(ifinfop);
    277 }
    278 
    279 /*
    280  * Check if `buf' has a NUL byte in its first `bufsize' bytes.
    281  */
    282 static boolean_t
    283 hasnulbyte(const char *buf, size_t bufsize)
    284 {
    285 	while (bufsize-- > 0) {
    286 		if (buf[bufsize] == '\0')
    287 			return (B_TRUE);
    288 	}
    289 	return (B_FALSE);
    290 }
    291 
    292 /*
    293  * Check that the TLV triplet named by `type', `len' and `value' is correctly
    294  * formed.
    295  */
    296 static boolean_t
    297 ipmp_checktlv(ipmp_infotype_t type, size_t len, void *value)
    298 {
    299 	ipmp_iflist_t		*iflistp;
    300 	ipmp_ifinfo_t		*ifinfop;
    301 	ipmp_grouplist_t	*grlistp;
    302 	ipmp_groupinfo_t	*grinfop;
    303 	unsigned int		i;
    304 
    305 	switch (type) {
    306 	case IPMP_IFLIST:
    307 		iflistp = (ipmp_iflist_t *)value;
    308 		if (len < IPMP_IFLIST_MINSIZE ||
    309 		    len < IPMP_IFLIST_SIZE(iflistp->il_nif))
    310 			return (B_FALSE);
    311 
    312 		for (i = 0; i < iflistp->il_nif; i++)
    313 			if (!hasnulbyte(iflistp->il_ifs[i], LIFNAMSIZ))
    314 				return (B_FALSE);
    315 		break;
    316 
    317 	case IPMP_IFINFO:
    318 		ifinfop = (ipmp_ifinfo_t *)value;
    319 		if (len != sizeof (ipmp_ifinfo_t))
    320 			return (B_FALSE);
    321 
    322 		if (!hasnulbyte(ifinfop->if_name, LIFNAMSIZ) ||
    323 		    !hasnulbyte(ifinfop->if_group, LIFGRNAMSIZ))
    324 			return (B_FALSE);
    325 		break;
    326 
    327 	case IPMP_GROUPLIST:
    328 		grlistp = (ipmp_grouplist_t *)value;
    329 		if (len < IPMP_GROUPLIST_MINSIZE ||
    330 		    len < IPMP_GROUPLIST_SIZE(grlistp->gl_ngroup))
    331 			return (B_FALSE);
    332 
    333 		for (i = 0; i < grlistp->gl_ngroup; i++)
    334 			if (!hasnulbyte(grlistp->gl_groups[i], LIFGRNAMSIZ))
    335 				return (B_FALSE);
    336 		break;
    337 
    338 	case IPMP_GROUPINFO:
    339 		grinfop = (ipmp_groupinfo_t *)value;
    340 		if (len != sizeof (ipmp_groupinfo_t))
    341 			return (B_FALSE);
    342 
    343 		if (!hasnulbyte(grinfop->gr_name, LIFGRNAMSIZ))
    344 			return (B_FALSE);
    345 		break;
    346 
    347 	case IPMP_SNAP:
    348 		if (len != sizeof (ipmp_snap_t))
    349 			return (B_FALSE);
    350 		break;
    351 
    352 	default:
    353 		return (B_FALSE);
    354 	}
    355 
    356 	return (B_TRUE);
    357 }
    358 
    359 /*
    360  * Create a group list with signature `sig' containing `ngroup' groups named
    361  * by `groups'.  Returns a pointer to the new group list on success, or NULL
    362  * on failure.
    363  */
    364 ipmp_grouplist_t *
    365 ipmp_grouplist_create(uint64_t sig, unsigned int ngroup,
    366     char (*groups)[LIFGRNAMSIZ])
    367 {
    368 	unsigned int i;
    369 	ipmp_grouplist_t *grlistp;
    370 
    371 	grlistp = malloc(IPMP_GROUPLIST_SIZE(ngroup));
    372 	if (grlistp == NULL)
    373 		return (NULL);
    374 
    375 	grlistp->gl_sig = sig;
    376 	grlistp->gl_ngroup = ngroup;
    377 	for (i = 0; i < ngroup; i++)
    378 		(void) strlcpy(grlistp->gl_groups[i], groups[i], LIFGRNAMSIZ);
    379 
    380 	return (grlistp);
    381 }
    382 
    383 /*
    384  * Clone the group list named by `grlistp'.  Returns a pointer to the clone on
    385  * success, or NULL on failure.
    386  */
    387 ipmp_grouplist_t *
    388 ipmp_grouplist_clone(ipmp_grouplist_t *grlistp)
    389 {
    390 	return (ipmp_grouplist_create(grlistp->gl_sig, grlistp->gl_ngroup,
    391 	    grlistp->gl_groups));
    392 }
    393 
    394 /*
    395  * Create an interface information structure for interface `name' and
    396  * associate `group', `state' and `type' with it.  Returns a pointer to the
    397  * interface information on success, or NULL on failure.
    398  */
    399 ipmp_ifinfo_t *
    400 ipmp_ifinfo_create(const char *name, const char *group, ipmp_if_state_t state,
    401     ipmp_if_type_t type)
    402 {
    403 	ipmp_ifinfo_t *ifinfop;
    404 
    405 	ifinfop = malloc(sizeof (ipmp_ifinfo_t));
    406 	if (ifinfop == NULL)
    407 		return (NULL);
    408 
    409 	(void) strlcpy(ifinfop->if_name, name, LIFNAMSIZ);
    410 	(void) strlcpy(ifinfop->if_group, group, LIFGRNAMSIZ);
    411 	ifinfop->if_state = state;
    412 	ifinfop->if_type = type;
    413 
    414 	return (ifinfop);
    415 }
    416 
    417 /*
    418  * Clone the interface information named by `ifinfop'.  Returns a pointer to
    419  * the clone on success, or NULL on failure.
    420  */
    421 ipmp_ifinfo_t *
    422 ipmp_ifinfo_clone(ipmp_ifinfo_t *ifinfop)
    423 {
    424 	return (ipmp_ifinfo_create(ifinfop->if_name, ifinfop->if_group,
    425 	    ifinfop->if_state, ifinfop->if_type));
    426 }
    427 
    428 /*
    429  * Create a group named `name' with signature `sig', in state `state', and
    430  * with the `nif' interfaces named by `ifs' as members.  Returns a pointer
    431  * to the new group on success, or NULL on failure.
    432  */
    433 ipmp_groupinfo_t *
    434 ipmp_groupinfo_create(const char *name, uint64_t sig, ipmp_group_state_t state,
    435     unsigned int nif, char (*ifs)[LIFNAMSIZ])
    436 {
    437 	ipmp_groupinfo_t *grinfop;
    438 	ipmp_iflist_t	*iflistp;
    439 	unsigned int	i;
    440 
    441 	grinfop = malloc(sizeof (ipmp_groupinfo_t));
    442 	if (grinfop == NULL)
    443 		return (NULL);
    444 
    445 	iflistp = malloc(IPMP_IFLIST_SIZE(nif));
    446 	if (iflistp == NULL) {
    447 		free(grinfop);
    448 		return (NULL);
    449 	}
    450 
    451 	grinfop->gr_sig = sig;
    452 	grinfop->gr_state = state;
    453 	grinfop->gr_iflistp = iflistp;
    454 	(void) strlcpy(grinfop->gr_name, name, LIFGRNAMSIZ);
    455 
    456 	iflistp->il_nif = nif;
    457 	for (i = 0; i < nif; i++)
    458 		(void) strlcpy(iflistp->il_ifs[i], ifs[i], LIFNAMSIZ);
    459 
    460 	return (grinfop);
    461 }
    462 
    463 /*
    464  * Clone the group information named by `grinfop'.  Returns a pointer to
    465  * the clone on success, or NULL on failure.
    466  */
    467 ipmp_groupinfo_t *
    468 ipmp_groupinfo_clone(ipmp_groupinfo_t *grinfop)
    469 {
    470 	return (ipmp_groupinfo_create(grinfop->gr_name, grinfop->gr_sig,
    471 	    grinfop->gr_state, grinfop->gr_iflistp->il_nif,
    472 	    grinfop->gr_iflistp->il_ifs));
    473 }
    474 
    475 /*
    476  * Set the query context associated with `handle' to `qcontext', which must be
    477  * either IPMP_QCONTEXT_LIVE or IPMP_QCONTEXT_SNAP.  Upon success, any
    478  * previous snapshot associated with `handle' is discarded.  Returns an IPMP
    479  * error code.
    480  */
    481 int
    482 ipmp_setqcontext(ipmp_handle_t handle, ipmp_qcontext_t qcontext)
    483 {
    484 	ipmp_state_t	*statep = handle;
    485 	ipmp_snap_t	*snap;
    486 	int		retval;
    487 
    488 	switch (qcontext) {
    489 	case IPMP_QCONTEXT_LIVE:
    490 		snap = NULL;
    491 		break;
    492 
    493 	case IPMP_QCONTEXT_SNAP:
    494 		retval = ipmp_snap_take(statep, &snap);
    495 		if (retval != IPMP_SUCCESS)
    496 			return (retval);
    497 		break;
    498 
    499 	default:
    500 		return (IPMP_EINVAL);
    501 	}
    502 
    503 	if (statep->st_snap != NULL)
    504 		ipmp_snap_free(statep->st_snap);
    505 	statep->st_snap = snap;
    506 
    507 	return (IPMP_SUCCESS);
    508 }
    509 
    510 /*
    511  * Create an empty snapshot.  Returns a pointer to the snapshot on success,
    512  * or NULL on failure.
    513  */
    514 ipmp_snap_t *
    515 ipmp_snap_create(void)
    516 {
    517 	ipmp_snap_t *snap;
    518 
    519 	snap = malloc(sizeof (ipmp_snap_t));
    520 	if (snap == NULL)
    521 		return (NULL);
    522 
    523 	snap->sn_grlistp = NULL;
    524 	snap->sn_grinfolistp = NULL;
    525 	snap->sn_ifinfolistp = NULL;
    526 	snap->sn_ngroup = 0;
    527 	snap->sn_nif = 0;
    528 
    529 	return (snap);
    530 }
    531 
    532 /*
    533  * Free all of the resources associated with snapshot `snap'.
    534  */
    535 void
    536 ipmp_snap_free(ipmp_snap_t *snap)
    537 {
    538 	ipmp_ifinfolist_t	*iflp, *ifnext;
    539 	ipmp_groupinfolist_t	*grlp, *grnext;
    540 
    541 	ipmp_freegrouplist(snap->sn_grlistp);
    542 
    543 	for (grlp = snap->sn_grinfolistp; grlp != NULL; grlp = grnext) {
    544 		grnext = grlp->grl_next;
    545 		ipmp_freegroupinfo(grlp->grl_grinfop);
    546 		free(grlp);
    547 	}
    548 
    549 	for (iflp = snap->sn_ifinfolistp; iflp != NULL; iflp = ifnext) {
    550 		ifnext = iflp->ifl_next;
    551 		ipmp_freeifinfo(iflp->ifl_ifinfop);
    552 		free(iflp);
    553 	}
    554 
    555 	free(snap);
    556 }
    557 
    558 /*
    559  * Add the group information in `grinfop' to the snapshot named by `snap'.
    560  * Returns an IPMP error code.
    561  */
    562 int
    563 ipmp_snap_addgroupinfo(ipmp_snap_t *snap, ipmp_groupinfo_t *grinfop)
    564 {
    565 	ipmp_groupinfolist_t *grlp;
    566 
    567 	/*
    568 	 * If the information for this group is already in the snapshot,
    569 	 * in.mpathd is broken.
    570 	 */
    571 	if (ipmp_snap_getgroupinfo(snap, grinfop->gr_name) != NULL)
    572 		return (IPMP_EPROTO);
    573 
    574 	grlp = malloc(sizeof (ipmp_groupinfolist_t));
    575 	if (grlp == NULL)
    576 		return (IPMP_ENOMEM);
    577 
    578 	grlp->grl_grinfop = grinfop;
    579 	grlp->grl_next = snap->sn_grinfolistp;
    580 	snap->sn_grinfolistp = grlp;
    581 	snap->sn_ngroup++;
    582 
    583 	return (IPMP_SUCCESS);
    584 }
    585 
    586 /*
    587  * Add the interface information in `ifinfop' to the snapshot named by `snap'.
    588  * Returns an IPMP error code.
    589  */
    590 int
    591 ipmp_snap_addifinfo(ipmp_snap_t *snap, ipmp_ifinfo_t *ifinfop)
    592 {
    593 	ipmp_ifinfolist_t *iflp;
    594 
    595 	/*
    596 	 * If the information for this interface is already in the snapshot,
    597 	 * in.mpathd is broken.
    598 	 */
    599 	if (ipmp_snap_getifinfo(snap, ifinfop->if_name) != NULL)
    600 		return (IPMP_EPROTO);
    601 
    602 	iflp = malloc(sizeof (ipmp_ifinfolist_t));
    603 	if (iflp == NULL)
    604 		return (IPMP_ENOMEM);
    605 
    606 	iflp->ifl_ifinfop = ifinfop;
    607 	iflp->ifl_next = snap->sn_ifinfolistp;
    608 	snap->sn_ifinfolistp = iflp;
    609 	snap->sn_nif++;
    610 
    611 	return (IPMP_SUCCESS);
    612 }
    613 
    614 /*
    615  * Retrieve the information for the group `name' in snapshot `snap'.
    616  * Returns a pointer to the group information on success, or NULL on failure.
    617  */
    618 static ipmp_groupinfo_t *
    619 ipmp_snap_getgroupinfo(ipmp_snap_t *snap, const char *name)
    620 {
    621 	ipmp_groupinfolist_t *grlp;
    622 
    623 	for (grlp = snap->sn_grinfolistp; grlp != NULL; grlp = grlp->grl_next) {
    624 		if (strcmp(grlp->grl_grinfop->gr_name, name) == 0)
    625 			break;
    626 	}
    627 
    628 	return (grlp != NULL ? grlp->grl_grinfop : NULL);
    629 }
    630 
    631 /*
    632  * Retrieve the information for the interface `name' in snapshot `snap'.
    633  * Returns a pointer to the interface information on success, or NULL on
    634  * failure.
    635  */
    636 static ipmp_ifinfo_t *
    637 ipmp_snap_getifinfo(ipmp_snap_t *snap, const char *name)
    638 {
    639 	ipmp_ifinfolist_t *iflp;
    640 
    641 	for (iflp = snap->sn_ifinfolistp; iflp != NULL; iflp = iflp->ifl_next) {
    642 		if (strcmp(iflp->ifl_ifinfop->if_name, name) == 0)
    643 			break;
    644 	}
    645 
    646 	return (iflp != NULL ? iflp->ifl_ifinfop : NULL);
    647 }
    648 
    649 /*
    650  * Using `statep', take a snapshot of the IPMP subsystem and if successful
    651  * return it in a dynamically allocated snapshot pointed to by `*snapp'.
    652  * Returns an IPMP error code.
    653  */
    654 static int
    655 ipmp_snap_take(ipmp_state_t *statep, ipmp_snap_t **snapp)
    656 {
    657 	ipmp_snap_t	*snap, *osnap;
    658 	ipmp_infotype_t	type;
    659 	ipmp_iflist_t	*iflistp;
    660 	int		retval;
    661 	size_t		len;
    662 	void		*infop;
    663 	struct timeval	end;
    664 
    665 	snap = ipmp_snap_create();
    666 	if (snap == NULL)
    667 		return (IPMP_ENOMEM);
    668 
    669 	retval = ipmp_sendquery(statep, IPMP_SNAP, NULL, &end);
    670 	if (retval != IPMP_SUCCESS) {
    671 		ipmp_snap_free(snap);
    672 		return (retval);
    673 	}
    674 
    675 	retval = ipmp_readinfo(statep, IPMP_SNAP, (void **)&osnap, &end);
    676 	if (retval != IPMP_SUCCESS) {
    677 		ipmp_snap_free(snap);
    678 		return (ipmp_querydone(statep, retval));
    679 	}
    680 
    681 	/*
    682 	 * Using the information in the passed `osnap' snapshot, build up our
    683 	 * own snapshot.  If we receive more than one grouplist, or more than
    684 	 * the expected number of interfaces or groups, then bail out.  Note
    685 	 * that there's only so much we can do to check that the information
    686 	 * sent by in.mpathd makes sense.  We know there will always be at
    687 	 * least one TLV (IPMP_GROUPLIST).
    688 	 */
    689 	do {
    690 		infop = NULL;
    691 		retval = ipmp_readtlv(statep->st_fd, &type, &len, &infop, &end);
    692 		if (retval != IPMP_SUCCESS)
    693 			goto fail;
    694 
    695 		if (!ipmp_checktlv(type, len, infop)) {
    696 			retval = IPMP_EPROTO;
    697 			goto fail;
    698 		}
    699 
    700 		switch (type) {
    701 		case IPMP_GROUPLIST:
    702 			if (snap->sn_grlistp != NULL) {
    703 				retval = IPMP_EPROTO;
    704 				break;
    705 			}
    706 			snap->sn_grlistp = infop;
    707 			break;
    708 
    709 		case IPMP_IFINFO:
    710 			if (snap->sn_nif == osnap->sn_nif) {
    711 				retval = IPMP_EPROTO;
    712 				break;
    713 			}
    714 			retval = ipmp_snap_addifinfo(snap, infop);
    715 			break;
    716 
    717 		case IPMP_GROUPINFO:
    718 			if (snap->sn_ngroup == osnap->sn_ngroup) {
    719 				retval = IPMP_EPROTO;
    720 				break;
    721 			}
    722 
    723 			/*
    724 			 * An IPMP_IFLIST TLV always follows the
    725 			 * IPMP_GROUPINFO TLV; read it in.
    726 			 */
    727 			retval = ipmp_readinfo(statep, IPMP_IFLIST,
    728 			    (void **)&iflistp, &end);
    729 			if (retval != IPMP_SUCCESS)
    730 				break;
    731 
    732 			((ipmp_groupinfo_t *)infop)->gr_iflistp = iflistp;
    733 			retval = ipmp_snap_addgroupinfo(snap, infop);
    734 			if (retval != IPMP_SUCCESS)
    735 				free(iflistp);
    736 			break;
    737 
    738 		default:
    739 			retval = IPMP_EPROTO;
    740 			break;
    741 		}
    742 fail:
    743 		if (retval != IPMP_SUCCESS) {
    744 			free(infop);
    745 			free(osnap);
    746 			ipmp_snap_free(snap);
    747 			return (ipmp_querydone(statep, retval));
    748 		}
    749 	} while (snap->sn_grlistp == NULL || snap->sn_nif < osnap->sn_nif ||
    750 	    snap->sn_ngroup < osnap->sn_ngroup);
    751 
    752 	free(osnap);
    753 	*snapp = snap;
    754 	return (ipmp_querydone(statep, IPMP_SUCCESS));
    755 }
    756