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