xref: /illumos-gate/usr/src/lib/libipmp/common/ipmp_query.c (revision f3682895b2a97c009685f16e8a4e5d3dc80e11f2)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  *
24  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
25  */
26 
27 /*
28  * IPMP query interfaces (see PSARC/2002/615 and PSARC/2007/272).
29  */
30 
31 #include <assert.h>
32 #include <errno.h>
33 #include <libinetutil.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 
39 #include "ipmp_impl.h"
40 #include "ipmp_mpathd.h"
41 #include "ipmp_query_impl.h"
42 
43 static ipmp_ifinfo_t	*ipmp_ifinfo_clone(ipmp_ifinfo_t *);
44 static ipmp_addrinfo_t	*ipmp_addrinfo_clone(ipmp_addrinfo_t *);
45 static ipmp_addrlist_t	*ipmp_addrlist_clone(ipmp_addrlist_t *);
46 static ipmp_grouplist_t	*ipmp_grouplist_clone(ipmp_grouplist_t *);
47 static ipmp_groupinfo_t	*ipmp_groupinfo_clone(ipmp_groupinfo_t *);
48 static ipmp_iflist_t	*ipmp_iflist_create(uint_t, char (*)[LIFNAMSIZ]);
49 static void		ipmp_freeiflist(ipmp_iflist_t *);
50 static ipmp_addrlist_t *ipmp_addrlist_create(uint_t, struct sockaddr_storage *);
51 static void		ipmp_freeaddrlist(ipmp_addrlist_t *);
52 static ipmp_groupinfo_t *ipmp_snap_getgroupinfo(ipmp_snap_t *, const char *);
53 static ipmp_ifinfo_t	*ipmp_snap_getifinfo(ipmp_snap_t *, const char *);
54 static ipmp_addrinfo_t  *ipmp_snap_getaddrinfo(ipmp_snap_t *, const char *,
55 			    struct sockaddr_storage *);
56 static int		ipmp_snap_take(ipmp_state_t *, ipmp_snap_t **);
57 static boolean_t	ipmp_checktlv(ipmp_infotype_t, size_t, void *);
58 static int		ipmp_querydone(ipmp_state_t *, int);
59 
60 /*
61  * Using `statep', send a query request for `type' to in.mpathd, and if
62  * necessary wait until at least `endtp' for a response.  Returns an IPMP
63  * error code.  If successful, the caller may then read additional query
64  * information through ipmp_readinfo(), and must eventually call
65  * ipmp_querydone() to complete the query operation.  Only one query may be
66  * outstanding on a given `statep' at a time.
67  */
68 static int
69 ipmp_sendquery(ipmp_state_t *statep, ipmp_infotype_t type, const char *name,
70     const void *addr, struct timeval *endtp)
71 {
72 	mi_query_t	query;
73 	mi_result_t	result;
74 	int		retval;
75 
76 	query.miq_command = MI_QUERY;
77 	query.miq_inforeq = type;
78 
79 	switch (type) {
80 	case IPMP_ADDRINFO:
81 		(void) strlcpy(query.miq_grname, name, LIFGRNAMSIZ);
82 		query.miq_addr = *(struct sockaddr_storage *)addr;
83 		break;
84 
85 	case IPMP_GROUPINFO:
86 		(void) strlcpy(query.miq_grname, name, LIFGRNAMSIZ);
87 		break;
88 
89 	case IPMP_IFINFO:
90 		(void) strlcpy(query.miq_ifname, name, LIFNAMSIZ);
91 		break;
92 
93 	case IPMP_GROUPLIST:
94 	case IPMP_SNAP:
95 		break;
96 
97 	default:
98 		assert(0);
99 	}
100 
101 	if (gettimeofday(endtp, NULL) == -1)
102 		return (IPMP_FAILURE);
103 
104 	endtp->tv_sec += IPMP_REQTIMEOUT;
105 
106 	assert(statep->st_fd == -1);
107 	retval = ipmp_connect(&statep->st_fd);
108 	if (retval != IPMP_SUCCESS)
109 		return (retval);
110 
111 	retval = ipmp_write(statep->st_fd, &query, sizeof (query));
112 	if (retval != IPMP_SUCCESS)
113 		return (ipmp_querydone(statep, retval));
114 
115 	retval = ipmp_read(statep->st_fd, &result, sizeof (result), endtp);
116 	if (retval != IPMP_SUCCESS)
117 		return (ipmp_querydone(statep, retval));
118 
119 	if (result.me_mpathd_error != IPMP_SUCCESS)
120 		return (ipmp_querydone(statep, result.me_mpathd_error));
121 
122 	return (IPMP_SUCCESS);
123 }
124 
125 /*
126  * Using `statep', read a query response of type `infotype' into a dynamically
127  * allocated buffer pointed to by `*infop', before the current time becomes
128  * `endtp'.  Returns an IPMP error code.
129  */
130 static int
131 ipmp_readinfo(ipmp_state_t *statep, ipmp_infotype_t infotype, void **infop,
132     const struct timeval *endtp)
133 {
134 	int		retval;
135 	size_t		len;
136 	ipmp_infotype_t	type;
137 
138 	retval = ipmp_readtlv(statep->st_fd, &type, &len, infop, endtp);
139 	if (retval != IPMP_SUCCESS)
140 		return (retval);
141 
142 	if (type != infotype || !ipmp_checktlv(type, len, *infop)) {
143 		free(*infop);
144 		return (IPMP_EPROTO);
145 	}
146 
147 	return (IPMP_SUCCESS);
148 }
149 
150 /*
151  * Using `statep', read in the remaining IPMP group information TLVs from
152  * in.mpathd into `grinfop' before the current time becomes `endtp'.  Returns
153  * an IPMP error code.  On failure, `grinfop' will have its original contents.
154  */
155 static int
156 ipmp_readgroupinfo_lists(ipmp_state_t *statep, ipmp_groupinfo_t *grinfop,
157     const struct timeval *endtp)
158 {
159 	int retval;
160 	ipmp_iflist_t *iflistp;
161 	ipmp_addrlist_t *adlistp;
162 
163 	retval = ipmp_readinfo(statep, IPMP_IFLIST, (void **)&iflistp, endtp);
164 	if (retval != IPMP_SUCCESS)
165 		return (retval);
166 
167 	retval = ipmp_readinfo(statep, IPMP_ADDRLIST, (void **)&adlistp, endtp);
168 	if (retval != IPMP_SUCCESS) {
169 		ipmp_freeiflist(iflistp);
170 		return (retval);
171 	}
172 
173 	grinfop->gr_iflistp = iflistp;
174 	grinfop->gr_adlistp = adlistp;
175 	return (IPMP_SUCCESS);
176 }
177 
178 /*
179  * Using `statep', read in the remaining IPMP interface information TLVs from
180  * in.mpathd into `ifinfop' before the current time becomes `endtp'.  Returns
181  * an IPMP error code.  On failure, `ifinfop' will have its original contents.
182  */
183 static int
184 ipmp_readifinfo_lists(ipmp_state_t *statep, ipmp_ifinfo_t *ifinfop,
185     const struct timeval *endtp)
186 {
187 	int retval;
188 	ipmp_addrlist_t *tlist4p, *tlist6p;
189 
190 	retval = ipmp_readinfo(statep, IPMP_ADDRLIST, (void **)&tlist4p, endtp);
191 	if (retval != IPMP_SUCCESS)
192 		return (retval);
193 
194 	retval = ipmp_readinfo(statep, IPMP_ADDRLIST, (void **)&tlist6p, endtp);
195 	if (retval != IPMP_SUCCESS) {
196 		ipmp_freeaddrlist(tlist4p);
197 		return (retval);
198 	}
199 
200 	ifinfop->if_targinfo4.it_targlistp = tlist4p;
201 	ifinfop->if_targinfo6.it_targlistp = tlist6p;
202 	return (IPMP_SUCCESS);
203 }
204 
205 /*
206  * Complete the query operation started in ipmp_sendquery().  The interface is
207  * designed to be easy to use in the `return' statement of a function, and
208  * thus returns the passed in `retval' and preserves `errno'.
209  */
210 static int
211 ipmp_querydone(ipmp_state_t *statep, int retval)
212 {
213 	int error = errno;
214 
215 	(void) close(statep->st_fd);
216 	statep->st_fd = -1;
217 	errno = error;
218 	return (retval);
219 }
220 
221 /*
222  * Using `handle', get the group list and store the results in a dynamically
223  * allocated buffer pointed to by `*grlistpp'.  Returns an IPMP error code.
224  */
225 int
226 ipmp_getgrouplist(ipmp_handle_t handle, ipmp_grouplist_t **grlistpp)
227 {
228 	ipmp_state_t	*statep = handle;
229 	struct timeval	end;
230 	int		retval;
231 
232 	if (statep->st_snap != NULL) {
233 		*grlistpp = ipmp_grouplist_clone(statep->st_snap->sn_grlistp);
234 		return (*grlistpp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM);
235 	}
236 
237 	retval = ipmp_sendquery(statep, IPMP_GROUPLIST, NULL, NULL, &end);
238 	if (retval != IPMP_SUCCESS)
239 		return (retval);
240 
241 	retval = ipmp_readinfo(statep, IPMP_GROUPLIST, (void **)grlistpp, &end);
242 	return (ipmp_querydone(statep, retval));
243 }
244 
245 /*
246  * Free the group list pointed to by `grlistp'.
247  */
248 void
249 ipmp_freegrouplist(ipmp_grouplist_t *grlistp)
250 {
251 	free(grlistp);
252 }
253 
254 /*
255  * Convert a ipmp_groupinfo_xfer_t used for communication with in.mpathd
256  * into a newly allocated ipmp_groupinfo_t. Free the ipmp_groupinfo_xfer_t,
257  * regardless of failure.
258  */
259 static ipmp_groupinfo_t *
260 ipmp_convertgroupinfo(ipmp_groupinfo_xfer_t *grxferp)
261 {
262 	ipmp_groupinfo_t *grinfop;
263 
264 	grinfop = calloc(1, sizeof (ipmp_groupinfo_t));
265 	if (grinfop != NULL) {
266 		memcpy(grinfop->gr_name, grxferp->grx_name,
267 		    sizeof (grinfop->gr_name));
268 		grinfop->gr_sig = grxferp->grx_sig;
269 		grinfop->gr_state = grxferp->grx_state;
270 		memcpy(grinfop->gr_ifname, grxferp->grx_ifname,
271 		    sizeof (grinfop->gr_ifname));
272 		memcpy(grinfop->gr_m4ifname, grxferp->grx_m4ifname,
273 		    sizeof (grinfop->gr_m4ifname));
274 		memcpy(grinfop->gr_m6ifname, grxferp->grx_m6ifname,
275 		    sizeof (grinfop->gr_m6ifname));
276 		memcpy(grinfop->gr_bcifname, grxferp->grx_bcifname,
277 		    sizeof (grinfop->gr_bcifname));
278 		grinfop->gr_fdt = grxferp->grx_fdt;
279 	}
280 
281 	free(grxferp);
282 
283 	return (grinfop);
284 }
285 /*
286  * Using `handle', get the group information associated with group `name' and
287  * store the results in a dynamically allocated buffer pointed to by
288  * `*grinfopp'.  Returns an IPMP error code.
289  */
290 int
291 ipmp_getgroupinfo(ipmp_handle_t handle, const char *name,
292     ipmp_groupinfo_t **grinfopp)
293 {
294 	ipmp_state_t	*statep = handle;
295 	int		retval;
296 	struct timeval	end;
297 	ipmp_groupinfo_t *grinfop;
298 	ipmp_groupinfo_xfer_t *grxferp;
299 
300 	if (statep->st_snap != NULL) {
301 		grinfop = ipmp_snap_getgroupinfo(statep->st_snap, name);
302 		if (grinfop == NULL)
303 			return (IPMP_EUNKGROUP);
304 
305 		*grinfopp = ipmp_groupinfo_clone(grinfop);
306 		return (*grinfopp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM);
307 	}
308 
309 	retval = ipmp_sendquery(statep, IPMP_GROUPINFO, name, NULL, &end);
310 	if (retval != IPMP_SUCCESS)
311 		return (retval);
312 
313 	retval = ipmp_readinfo(statep, IPMP_GROUPINFO, (void **)&grxferp, &end);
314 	if (retval != IPMP_SUCCESS)
315 		return (ipmp_querydone(statep, retval));
316 
317 	*grinfopp = ipmp_convertgroupinfo(grxferp);
318 	if (*grinfopp == NULL)
319 		return (ipmp_querydone(statep, IPMP_ENOMEM));
320 
321 	retval = ipmp_readgroupinfo_lists(statep, *grinfopp, &end);
322 	if (retval != IPMP_SUCCESS)
323 		free(*grinfopp);
324 
325 	return (ipmp_querydone(statep, retval));
326 }
327 
328 /*
329  * Free the group information pointed to by `grinfop'.
330  */
331 void
332 ipmp_freegroupinfo(ipmp_groupinfo_t *grinfop)
333 {
334 	ipmp_freeaddrlist(grinfop->gr_adlistp);
335 	ipmp_freeiflist(grinfop->gr_iflistp);
336 	free(grinfop);
337 }
338 
339 /*
340  * Convert a ipmp_ifinfo_xfer_t used for communication with in.mpathd
341  * into a newly allocated ipmp_ifinfo_t. Free the ipmp_ifinfo_xfer_t,
342  * regardless of failure.
343  */
344 static ipmp_ifinfo_t *
345 ipmp_convertifinfo(ipmp_ifinfo_xfer_t *ifxferp)
346 {
347 	ipmp_ifinfo_t *ifinfop;
348 
349 	ifinfop = calloc(1, sizeof (ipmp_ifinfo_t));
350 	if (ifinfop != NULL) {
351 		memcpy(ifinfop->if_name, ifxferp->ifx_name,
352 		    sizeof (ifinfop->if_name));
353 		memcpy(ifinfop->if_group, ifxferp->ifx_group,
354 		    sizeof (ifinfop->if_group));
355 		ifinfop->if_state = ifxferp->ifx_state;
356 		ifinfop->if_type = ifxferp->ifx_type;
357 		ifinfop->if_linkstate = ifxferp->ifx_linkstate;
358 		ifinfop->if_probestate = ifxferp->ifx_probestate;
359 		ifinfop->if_flags = ifxferp->ifx_flags;
360 		memcpy(ifinfop->if_targinfo4.it_name,
361 		    ifxferp->ifx_targinfo4.itx_name,
362 		    sizeof (ifinfop->if_targinfo4.it_name));
363 		ifinfop->if_targinfo4.it_testaddr =
364 		    ifxferp->ifx_targinfo4.itx_testaddr;
365 		ifinfop->if_targinfo4.it_targmode =
366 		    ifxferp->ifx_targinfo4.itx_targmode;
367 		memcpy(ifinfop->if_targinfo6.it_name,
368 		    ifxferp->ifx_targinfo6.itx_name,
369 		    sizeof (ifinfop->if_targinfo6.it_name));
370 		ifinfop->if_targinfo6.it_testaddr =
371 		    ifxferp->ifx_targinfo6.itx_testaddr;
372 		ifinfop->if_targinfo6.it_targmode =
373 		    ifxferp->ifx_targinfo6.itx_targmode;
374 	}
375 
376 	free(ifxferp);
377 
378 	return (ifinfop);
379 }
380 
381 /*
382  * Using `handle', get the interface information associated with interface
383  * `name' and store the results in a dynamically allocated buffer pointed to
384  * by `*ifinfopp'.  Returns an IPMP error code.
385  */
386 int
387 ipmp_getifinfo(ipmp_handle_t handle, const char *name, ipmp_ifinfo_t **ifinfopp)
388 {
389 	ipmp_state_t	*statep = handle;
390 	ipmp_ifinfo_t	*ifinfop;
391 	ipmp_ifinfo_xfer_t *ifxferp;
392 	int		retval;
393 	struct timeval	end;
394 
395 	if (statep->st_snap != NULL) {
396 		ifinfop = ipmp_snap_getifinfo(statep->st_snap, name);
397 		if (ifinfop == NULL)
398 			return (IPMP_EUNKIF);
399 
400 		*ifinfopp = ipmp_ifinfo_clone(ifinfop);
401 		return (*ifinfopp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM);
402 	}
403 
404 	retval = ipmp_sendquery(statep, IPMP_IFINFO, name, NULL, &end);
405 	if (retval != IPMP_SUCCESS)
406 		return (retval);
407 
408 	retval = ipmp_readinfo(statep, IPMP_IFINFO, (void **)&ifxferp, &end);
409 	if (retval != IPMP_SUCCESS)
410 		return (ipmp_querydone(statep, retval));
411 
412 	*ifinfopp = ipmp_convertifinfo(ifxferp);
413 	if (*ifinfopp == NULL)
414 		return (ipmp_querydone(statep, IPMP_ENOMEM));
415 
416 	retval = ipmp_readifinfo_lists(statep, *ifinfopp, &end);
417 	if (retval != IPMP_SUCCESS)
418 		free(*ifinfopp);
419 
420 	return (ipmp_querydone(statep, retval));
421 }
422 
423 /*
424  * Free the interface information pointed to by `ifinfop'.
425  */
426 void
427 ipmp_freeifinfo(ipmp_ifinfo_t *ifinfop)
428 {
429 	ipmp_freeaddrlist(ifinfop->if_targinfo4.it_targlistp);
430 	ipmp_freeaddrlist(ifinfop->if_targinfo6.it_targlistp);
431 	free(ifinfop);
432 }
433 
434 /*
435  * Using `handle', get the address information associated with address `addrp'
436  * on group `grname' and store the results in a dynamically allocated buffer
437  * pointed to by `*adinfopp'.  Returns an IPMP error code.
438  */
439 int
440 ipmp_getaddrinfo(ipmp_handle_t handle, const char *grname,
441     struct sockaddr_storage *addrp, ipmp_addrinfo_t **adinfopp)
442 {
443 	ipmp_state_t	*statep = handle;
444 	ipmp_addrinfo_t	*adinfop;
445 	int		retval;
446 	struct timeval	end;
447 
448 	if (statep->st_snap != NULL) {
449 		adinfop = ipmp_snap_getaddrinfo(statep->st_snap, grname, addrp);
450 		if (adinfop == NULL)
451 			return (IPMP_EUNKADDR);
452 
453 		*adinfopp = ipmp_addrinfo_clone(adinfop);
454 		return (*adinfopp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM);
455 	}
456 
457 	retval = ipmp_sendquery(statep, IPMP_ADDRINFO, grname, addrp, &end);
458 	if (retval != IPMP_SUCCESS)
459 		return (retval);
460 
461 	retval = ipmp_readinfo(statep, IPMP_ADDRINFO, (void **)adinfopp, &end);
462 	return (ipmp_querydone(statep, retval));
463 }
464 
465 /*
466  * Free the address information pointed to by `adinfop'.
467  */
468 void
469 ipmp_freeaddrinfo(ipmp_addrinfo_t *adinfop)
470 {
471 	free(adinfop);
472 }
473 
474 /*
475  * Check if `buf' has a NUL byte in its first `bufsize' bytes.
476  */
477 static boolean_t
478 hasnulbyte(const char *buf, size_t bufsize)
479 {
480 	while (bufsize-- > 0) {
481 		if (buf[bufsize] == '\0')
482 			return (B_TRUE);
483 	}
484 	return (B_FALSE);
485 }
486 
487 /*
488  * Check that the TLV triplet named by `type', `len' and `value' is correctly
489  * formed.
490  */
491 static boolean_t
492 ipmp_checktlv(ipmp_infotype_t type, size_t len, void *value)
493 {
494 	ipmp_iflist_t		*iflistp;
495 	ipmp_ifinfo_xfer_t	*ifxferp;
496 	ipmp_grouplist_t	*grlistp;
497 	ipmp_groupinfo_xfer_t	*grxferp;
498 	ipmp_addrlist_t		*adlistp;
499 	unsigned int		i;
500 
501 	switch (type) {
502 	case IPMP_ADDRINFO:
503 		if (len != sizeof (ipmp_addrinfo_t))
504 			return (B_FALSE);
505 		break;
506 
507 	case IPMP_ADDRLIST:
508 		adlistp = (ipmp_addrlist_t *)value;
509 		if (len < IPMP_ADDRLIST_SIZE(0) ||
510 		    len < IPMP_ADDRLIST_SIZE(adlistp->al_naddr))
511 			return (B_FALSE);
512 		break;
513 
514 	case IPMP_IFLIST:
515 		iflistp = (ipmp_iflist_t *)value;
516 		if (len < IPMP_IFLIST_SIZE(0) ||
517 		    len < IPMP_IFLIST_SIZE(iflistp->il_nif))
518 			return (B_FALSE);
519 
520 		for (i = 0; i < iflistp->il_nif; i++)
521 			if (!hasnulbyte(iflistp->il_ifs[i], LIFNAMSIZ))
522 				return (B_FALSE);
523 		break;
524 
525 	case IPMP_IFINFO:
526 		ifxferp = (ipmp_ifinfo_xfer_t *)value;
527 		if (len != sizeof (ipmp_ifinfo_xfer_t))
528 			return (B_FALSE);
529 
530 		if (!hasnulbyte(ifxferp->ifx_name, LIFNAMSIZ) ||
531 		    !hasnulbyte(ifxferp->ifx_group, LIFGRNAMSIZ))
532 			return (B_FALSE);
533 		break;
534 
535 	case IPMP_GROUPLIST:
536 		grlistp = (ipmp_grouplist_t *)value;
537 		if (len < IPMP_GROUPLIST_SIZE(0) ||
538 		    len < IPMP_GROUPLIST_SIZE(grlistp->gl_ngroup))
539 			return (B_FALSE);
540 
541 		for (i = 0; i < grlistp->gl_ngroup; i++)
542 			if (!hasnulbyte(grlistp->gl_groups[i], LIFGRNAMSIZ))
543 				return (B_FALSE);
544 		break;
545 
546 	case IPMP_GROUPINFO:
547 		grxferp = (ipmp_groupinfo_xfer_t *)value;
548 		if (len != sizeof (ipmp_groupinfo_xfer_t))
549 			return (B_FALSE);
550 
551 		if (!hasnulbyte(grxferp->grx_name, LIFGRNAMSIZ))
552 			return (B_FALSE);
553 		break;
554 
555 	case IPMP_ADDRCNT:
556 	case IPMP_GROUPCNT:
557 	case IPMP_IFCNT:
558 		if (len != sizeof (uint32_t))
559 			return (B_FALSE);
560 		break;
561 
562 	default:
563 		return (B_FALSE);
564 	}
565 
566 	return (B_TRUE);
567 }
568 
569 /*
570  * Create a group list; arguments match ipmp_grouplist_t fields.  Returns a
571  * pointer to the new group list on success, or NULL on failure.
572  */
573 ipmp_grouplist_t *
574 ipmp_grouplist_create(uint64_t sig, unsigned int ngroup,
575     char (*groups)[LIFGRNAMSIZ])
576 {
577 	unsigned int i;
578 	ipmp_grouplist_t *grlistp;
579 
580 	grlistp = malloc(IPMP_GROUPLIST_SIZE(ngroup));
581 	if (grlistp == NULL)
582 		return (NULL);
583 
584 	grlistp->gl_sig = sig;
585 	grlistp->gl_ngroup = ngroup;
586 	for (i = 0; i < ngroup; i++)
587 		(void) strlcpy(grlistp->gl_groups[i], groups[i], LIFGRNAMSIZ);
588 
589 	return (grlistp);
590 }
591 
592 /*
593  * Clone the group list named by `grlistp'.  Returns a pointer to the clone on
594  * success, or NULL on failure.
595  */
596 ipmp_grouplist_t *
597 ipmp_grouplist_clone(ipmp_grouplist_t *grlistp)
598 {
599 	return (ipmp_grouplist_create(grlistp->gl_sig, grlistp->gl_ngroup,
600 	    grlistp->gl_groups));
601 }
602 
603 /*
604  * Create target information; arguments match ipmp_targinfo_t fields.  Returns
605  * a pointer to the new target info on success, or NULL on failure.
606  */
607 ipmp_targinfo_t *
608 ipmp_targinfo_create(const char *name, struct sockaddr_storage *testaddrp,
609     ipmp_if_targmode_t targmode, uint_t ntarg, struct sockaddr_storage *targs)
610 {
611 	ipmp_targinfo_t *targinfop;
612 
613 	targinfop = malloc(sizeof (ipmp_targinfo_t));
614 	if (targinfop == NULL)
615 		return (NULL);
616 
617 	targinfop->it_testaddr = *testaddrp;
618 	targinfop->it_targmode = targmode;
619 	targinfop->it_targlistp = ipmp_addrlist_create(ntarg, targs);
620 	if (targinfop->it_targlistp == NULL) {
621 		ipmp_freetarginfo(targinfop);
622 		return (NULL);
623 	}
624 	(void) strlcpy(targinfop->it_name, name, LIFNAMSIZ);
625 
626 	return (targinfop);
627 }
628 
629 /*
630  * Free the target information pointed to by `targinfop'.
631  */
632 void
633 ipmp_freetarginfo(ipmp_targinfo_t *targinfop)
634 {
635 	free(targinfop->it_targlistp);
636 	free(targinfop);
637 }
638 
639 /*
640  * Create an interface list; arguments match ipmp_iflist_t fields.  Returns a
641  * pointer to the new interface list on success, or NULL on failure.
642  */
643 static ipmp_iflist_t *
644 ipmp_iflist_create(uint_t nif, char (*ifs)[LIFNAMSIZ])
645 {
646 	unsigned int i;
647 	ipmp_iflist_t *iflistp;
648 
649 	iflistp = malloc(IPMP_IFLIST_SIZE(nif));
650 	if (iflistp == NULL)
651 		return (NULL);
652 
653 	iflistp->il_nif = nif;
654 	for (i = 0; i < nif; i++)
655 		(void) strlcpy(iflistp->il_ifs[i], ifs[i], LIFNAMSIZ);
656 
657 	return (iflistp);
658 }
659 
660 /*
661  * Free the interface list pointed to by `iflistp'.
662  */
663 static void
664 ipmp_freeiflist(ipmp_iflist_t *iflistp)
665 {
666 	free(iflistp);
667 }
668 
669 /*
670  * Create an interface; arguments match ipmp_ifinfo_t fields.  Returns a
671  * pointer to the new interface on success, or NULL on failure.
672  */
673 ipmp_ifinfo_t *
674 ipmp_ifinfo_create(const char *name, const char *group, ipmp_if_state_t state,
675     ipmp_if_type_t type, ipmp_if_linkstate_t linkstate,
676     ipmp_if_probestate_t probestate, ipmp_if_flags_t flags,
677     ipmp_targinfo_t *targinfo4p, ipmp_targinfo_t *targinfo6p)
678 {
679 	ipmp_ifinfo_t *ifinfop;
680 
681 	ifinfop = malloc(sizeof (ipmp_ifinfo_t));
682 	if (ifinfop == NULL)
683 		return (NULL);
684 
685 	(void) strlcpy(ifinfop->if_name, name, LIFNAMSIZ);
686 	(void) strlcpy(ifinfop->if_group, group, LIFGRNAMSIZ);
687 
688 	ifinfop->if_state	= state;
689 	ifinfop->if_type	= type;
690 	ifinfop->if_linkstate	= linkstate;
691 	ifinfop->if_probestate	= probestate;
692 	ifinfop->if_flags	= flags;
693 	ifinfop->if_targinfo4	= *targinfo4p;
694 	ifinfop->if_targinfo6	= *targinfo6p;
695 
696 	ifinfop->if_targinfo4.it_targlistp =
697 	    ipmp_addrlist_clone(targinfo4p->it_targlistp);
698 	ifinfop->if_targinfo6.it_targlistp =
699 	    ipmp_addrlist_clone(targinfo6p->it_targlistp);
700 
701 	if (ifinfop->if_targinfo4.it_targlistp == NULL ||
702 	    ifinfop->if_targinfo6.it_targlistp == NULL) {
703 		ipmp_freeifinfo(ifinfop);
704 		return (NULL);
705 	}
706 
707 	return (ifinfop);
708 }
709 
710 /*
711  * Clone the interface information named by `ifinfop'.  Returns a pointer to
712  * the clone on success, or NULL on failure.
713  */
714 ipmp_ifinfo_t *
715 ipmp_ifinfo_clone(ipmp_ifinfo_t *ifinfop)
716 {
717 	return (ipmp_ifinfo_create(ifinfop->if_name, ifinfop->if_group,
718 	    ifinfop->if_state, ifinfop->if_type, ifinfop->if_linkstate,
719 	    ifinfop->if_probestate, ifinfop->if_flags, &ifinfop->if_targinfo4,
720 	    &ifinfop->if_targinfo6));
721 }
722 
723 /*
724  * Create a group; arguments match ipmp_groupinfo_t fields.  Returns a pointer
725  * to the new group on success, or NULL on failure.
726  */
727 ipmp_groupinfo_t *
728 ipmp_groupinfo_create(const char *name, uint64_t sig, uint_t fdt,
729     ipmp_group_state_t state, uint_t nif, char (*ifs)[LIFNAMSIZ],
730     const char *grifname, const char *m4ifname, const char *m6ifname,
731     const char *bcifname, uint_t naddr, struct sockaddr_storage *addrs)
732 {
733 	ipmp_groupinfo_t *grinfop;
734 
735 	grinfop = malloc(sizeof (ipmp_groupinfo_t));
736 	if (grinfop == NULL)
737 		return (NULL);
738 
739 	grinfop->gr_sig	= sig;
740 	grinfop->gr_fdt = fdt;
741 	grinfop->gr_state = state;
742 	grinfop->gr_iflistp = ipmp_iflist_create(nif, ifs);
743 	grinfop->gr_adlistp = ipmp_addrlist_create(naddr, addrs);
744 	if (grinfop->gr_iflistp == NULL || grinfop->gr_adlistp == NULL) {
745 		ipmp_freegroupinfo(grinfop);
746 		return (NULL);
747 	}
748 	(void) strlcpy(grinfop->gr_name, name, LIFGRNAMSIZ);
749 	(void) strlcpy(grinfop->gr_ifname, grifname, LIFNAMSIZ);
750 	(void) strlcpy(grinfop->gr_m4ifname, m4ifname, LIFNAMSIZ);
751 	(void) strlcpy(grinfop->gr_m6ifname, m6ifname, LIFNAMSIZ);
752 	(void) strlcpy(grinfop->gr_bcifname, bcifname, LIFNAMSIZ);
753 
754 	return (grinfop);
755 }
756 
757 /*
758  * Clone the group information named by `grinfop'.  Returns a pointer to
759  * the clone on success, or NULL on failure.
760  */
761 ipmp_groupinfo_t *
762 ipmp_groupinfo_clone(ipmp_groupinfo_t *grinfop)
763 {
764 	ipmp_addrlist_t *adlistp = grinfop->gr_adlistp;
765 
766 	return (ipmp_groupinfo_create(grinfop->gr_name, grinfop->gr_sig,
767 	    grinfop->gr_fdt, grinfop->gr_state, grinfop->gr_iflistp->il_nif,
768 	    grinfop->gr_iflistp->il_ifs, grinfop->gr_ifname,
769 	    grinfop->gr_m4ifname, grinfop->gr_m6ifname, grinfop->gr_bcifname,
770 	    adlistp->al_naddr, adlistp->al_addrs));
771 }
772 
773 /*
774  * Create an address list; arguments match ipmp_addrlist_t fields.  Returns
775  * a pointer to the new address list on success, or NULL on failure.
776  */
777 static ipmp_addrlist_t *
778 ipmp_addrlist_create(uint_t naddr, struct sockaddr_storage *addrs)
779 {
780 	unsigned int i;
781 	ipmp_addrlist_t *adlistp;
782 
783 	adlistp = malloc(IPMP_ADDRLIST_SIZE(naddr));
784 	if (adlistp == NULL)
785 		return (NULL);
786 
787 	adlistp->al_naddr = naddr;
788 	for (i = 0; i < naddr; i++)
789 		adlistp->al_addrs[i] = addrs[i];
790 
791 	return (adlistp);
792 }
793 
794 /*
795  * Clone the address list named by `adlistp'.  Returns a pointer to the clone
796  * on success, or NULL on failure.
797  */
798 static ipmp_addrlist_t *
799 ipmp_addrlist_clone(ipmp_addrlist_t *adlistp)
800 {
801 	return (ipmp_addrlist_create(adlistp->al_naddr, adlistp->al_addrs));
802 }
803 
804 /*
805  * Free the address list pointed to by `adlistp'.
806  */
807 static void
808 ipmp_freeaddrlist(ipmp_addrlist_t *adlistp)
809 {
810 	free(adlistp);
811 }
812 
813 /*
814  * Create an address; arguments match ipmp_addrinfo_t fields.  Returns a
815  * pointer to the new address on success, or NULL on failure.
816  */
817 ipmp_addrinfo_t *
818 ipmp_addrinfo_create(struct sockaddr_storage *addrp, ipmp_addr_state_t state,
819     const char *group, const char *binding)
820 {
821 	ipmp_addrinfo_t *adinfop;
822 
823 	adinfop = malloc(sizeof (ipmp_addrinfo_t));
824 	if (adinfop == NULL)
825 		return (NULL);
826 
827 	adinfop->ad_addr = *addrp;
828 	adinfop->ad_state = state;
829 	(void) strlcpy(adinfop->ad_group, group, LIFGRNAMSIZ);
830 	(void) strlcpy(adinfop->ad_binding, binding, LIFNAMSIZ);
831 
832 	return (adinfop);
833 }
834 
835 /*
836  * Clone the address information named by `adinfop'.  Returns a pointer to
837  * the clone on success, or NULL on failure.
838  */
839 ipmp_addrinfo_t *
840 ipmp_addrinfo_clone(ipmp_addrinfo_t *adinfop)
841 {
842 	return (ipmp_addrinfo_create(&adinfop->ad_addr, adinfop->ad_state,
843 	    adinfop->ad_group, adinfop->ad_binding));
844 }
845 
846 /*
847  * Set the query context associated with `handle' to `qcontext', which must be
848  * either IPMP_QCONTEXT_LIVE or IPMP_QCONTEXT_SNAP.  Upon success, any
849  * previous snapshot associated with `handle' is discarded.  Returns an IPMP
850  * error code.
851  */
852 int
853 ipmp_setqcontext(ipmp_handle_t handle, ipmp_qcontext_t qcontext)
854 {
855 	ipmp_state_t	*statep = handle;
856 	ipmp_snap_t	*snap;
857 	int		retval;
858 
859 	switch (qcontext) {
860 	case IPMP_QCONTEXT_LIVE:
861 		snap = NULL;
862 		break;
863 
864 	case IPMP_QCONTEXT_SNAP:
865 		retval = ipmp_snap_take(statep, &snap);
866 		if (retval != IPMP_SUCCESS)
867 			return (retval);
868 		break;
869 
870 	default:
871 		return (IPMP_EINVAL);
872 	}
873 
874 	if (statep->st_snap != NULL)
875 		ipmp_snap_free(statep->st_snap);
876 	statep->st_snap = snap;
877 
878 	return (IPMP_SUCCESS);
879 }
880 
881 /*
882  * Create an empty snapshot.  Returns a pointer to the snapshot on success,
883  * or NULL on failure.
884  */
885 ipmp_snap_t *
886 ipmp_snap_create(void)
887 {
888 	ipmp_snap_t *snap;
889 
890 	snap = malloc(sizeof (ipmp_snap_t));
891 	if (snap == NULL)
892 		return (NULL);
893 
894 	snap->sn_grlistp = NULL;
895 	snap->sn_grinfolistp = NULL;
896 	snap->sn_ifinfolistp = NULL;
897 	snap->sn_adinfolistp = NULL;
898 	snap->sn_ngroup = 0;
899 	snap->sn_nif = 0;
900 	snap->sn_naddr = 0;
901 
902 	return (snap);
903 }
904 
905 /*
906  * Free all of the resources associated with snapshot `snap'.
907  */
908 void
909 ipmp_snap_free(ipmp_snap_t *snap)
910 {
911 	ipmp_ifinfolist_t	*iflp, *ifnext;
912 	ipmp_addrinfolist_t	*adlp, *adnext;
913 	ipmp_groupinfolist_t	*grlp, *grnext;
914 
915 	ipmp_freegrouplist(snap->sn_grlistp);
916 
917 	for (grlp = snap->sn_grinfolistp; grlp != NULL; grlp = grnext) {
918 		grnext = grlp->grl_next;
919 		ipmp_freegroupinfo(grlp->grl_grinfop);
920 		free(grlp);
921 	}
922 
923 	for (iflp = snap->sn_ifinfolistp; iflp != NULL; iflp = ifnext) {
924 		ifnext = iflp->ifl_next;
925 		ipmp_freeifinfo(iflp->ifl_ifinfop);
926 		free(iflp);
927 	}
928 
929 	for (adlp = snap->sn_adinfolistp; adlp != NULL; adlp = adnext) {
930 		adnext = adlp->adl_next;
931 		ipmp_freeaddrinfo(adlp->adl_adinfop);
932 		free(adlp);
933 	}
934 
935 	free(snap);
936 }
937 
938 /*
939  * Add the group information in `grinfop' to the snapshot named by `snap'.
940  * Returns an IPMP error code.
941  */
942 int
943 ipmp_snap_addgroupinfo(ipmp_snap_t *snap, ipmp_groupinfo_t *grinfop)
944 {
945 	ipmp_groupinfolist_t *grlp;
946 
947 	/*
948 	 * If the information for this group is already in the snapshot,
949 	 * in.mpathd is broken.
950 	 */
951 	if (ipmp_snap_getgroupinfo(snap, grinfop->gr_name) != NULL)
952 		return (IPMP_EPROTO);
953 
954 	grlp = malloc(sizeof (ipmp_groupinfolist_t));
955 	if (grlp == NULL)
956 		return (IPMP_ENOMEM);
957 
958 	grlp->grl_grinfop = grinfop;
959 	grlp->grl_next = snap->sn_grinfolistp;
960 	snap->sn_grinfolistp = grlp;
961 	snap->sn_ngroup++;
962 
963 	return (IPMP_SUCCESS);
964 }
965 
966 /*
967  * Add the interface information in `ifinfop' to the snapshot named by `snap'.
968  * Returns an IPMP error code.
969  */
970 int
971 ipmp_snap_addifinfo(ipmp_snap_t *snap, ipmp_ifinfo_t *ifinfop)
972 {
973 	ipmp_ifinfolist_t *iflp;
974 
975 	/*
976 	 * If the information for this interface is already in the snapshot,
977 	 * in.mpathd is broken.
978 	 */
979 	if (ipmp_snap_getifinfo(snap, ifinfop->if_name) != NULL)
980 		return (IPMP_EPROTO);
981 
982 	iflp = malloc(sizeof (ipmp_ifinfolist_t));
983 	if (iflp == NULL)
984 		return (IPMP_ENOMEM);
985 
986 	iflp->ifl_ifinfop = ifinfop;
987 	iflp->ifl_next = snap->sn_ifinfolistp;
988 	snap->sn_ifinfolistp = iflp;
989 	snap->sn_nif++;
990 
991 	return (IPMP_SUCCESS);
992 }
993 
994 /*
995  * Add the address information in `adinfop' to the snapshot named by `snap'.
996  * Returns an IPMP error code.
997  */
998 int
999 ipmp_snap_addaddrinfo(ipmp_snap_t *snap, ipmp_addrinfo_t *adinfop)
1000 {
1001 	ipmp_addrinfolist_t *adlp;
1002 
1003 	/*
1004 	 * Any duplicate addresses should've already been weeded by in.mpathd.
1005 	 */
1006 	if (ipmp_snap_getaddrinfo(snap, adinfop->ad_group,
1007 	    &adinfop->ad_addr) != NULL)
1008 		return (IPMP_EPROTO);
1009 
1010 	adlp = malloc(sizeof (ipmp_addrinfolist_t));
1011 	if (adlp == NULL)
1012 		return (IPMP_ENOMEM);
1013 
1014 	adlp->adl_adinfop = adinfop;
1015 	adlp->adl_next = snap->sn_adinfolistp;
1016 	snap->sn_adinfolistp = adlp;
1017 	snap->sn_naddr++;
1018 
1019 	return (IPMP_SUCCESS);
1020 }
1021 
1022 /*
1023  * Retrieve the information for the group `name' in snapshot `snap'.
1024  * Returns a pointer to the group information on success, or NULL on failure.
1025  */
1026 static ipmp_groupinfo_t *
1027 ipmp_snap_getgroupinfo(ipmp_snap_t *snap, const char *name)
1028 {
1029 	ipmp_groupinfolist_t *grlp;
1030 
1031 	for (grlp = snap->sn_grinfolistp; grlp != NULL; grlp = grlp->grl_next) {
1032 		if (strcmp(grlp->grl_grinfop->gr_name, name) == 0)
1033 			break;
1034 	}
1035 
1036 	return (grlp != NULL ? grlp->grl_grinfop : NULL);
1037 }
1038 
1039 /*
1040  * Retrieve the information for the interface `name' in snapshot `snap'.
1041  * Returns a pointer to the interface information on success, or NULL on
1042  * failure.
1043  */
1044 static ipmp_ifinfo_t *
1045 ipmp_snap_getifinfo(ipmp_snap_t *snap, const char *name)
1046 {
1047 	ipmp_ifinfolist_t *iflp;
1048 
1049 	for (iflp = snap->sn_ifinfolistp; iflp != NULL; iflp = iflp->ifl_next) {
1050 		if (strcmp(iflp->ifl_ifinfop->if_name, name) == 0)
1051 			break;
1052 	}
1053 
1054 	return (iflp != NULL ? iflp->ifl_ifinfop : NULL);
1055 }
1056 
1057 /*
1058  * Retrieve the information for the address `addrp' on group `grname' in
1059  * snapshot `snap'.  Returns a pointer to the address information on success,
1060  * or NULL on failure.
1061  */
1062 static ipmp_addrinfo_t *
1063 ipmp_snap_getaddrinfo(ipmp_snap_t *snap, const char *grname,
1064     struct sockaddr_storage *addrp)
1065 {
1066 	ipmp_addrinfolist_t *adlp;
1067 
1068 	for (adlp = snap->sn_adinfolistp; adlp != NULL; adlp = adlp->adl_next) {
1069 		if (strcmp(grname, adlp->adl_adinfop->ad_group) == 0 &&
1070 		    sockaddrcmp(addrp, &adlp->adl_adinfop->ad_addr))
1071 			break;
1072 	}
1073 
1074 	return (adlp != NULL ? adlp->adl_adinfop : NULL);
1075 }
1076 
1077 /*
1078  * Using `statep', take a snapshot of the IPMP subsystem and if successful
1079  * return it in a dynamically allocated snapshot pointed to by `*snapp'.
1080  * Returns an IPMP error code.
1081  */
1082 static int
1083 ipmp_snap_take(ipmp_state_t *statep, ipmp_snap_t **snapp)
1084 {
1085 	uint64_t	naddr, ngroup, nif;
1086 	ipmp_snap_t	*snap;
1087 	ipmp_infotype_t	type;
1088 	int		retval;
1089 	size_t		len;
1090 	void		*infop;
1091 	struct timeval	end;
1092 
1093 	naddr = ngroup = nif = UINT64_MAX;
1094 
1095 	snap = ipmp_snap_create();
1096 	if (snap == NULL)
1097 		return (IPMP_ENOMEM);
1098 
1099 	retval = ipmp_sendquery(statep, IPMP_SNAP, NULL, NULL, &end);
1100 	if (retval != IPMP_SUCCESS) {
1101 		ipmp_snap_free(snap);
1102 		return (retval);
1103 	}
1104 
1105 	/*
1106 	 * Build up our snapshot.  We know there will always be at least four
1107 	 * TLVs for IPMP_GROUPLIST, IPMP_IFCNT, IPMP_GROUPCNT, and IPMP_ADDRCNT.
1108 	 * If we receive anything illogical (e.g., more than the expected number
1109 	 * of interfaces), then bail out.  However, to a large extent we have to
1110 	 * trust the information sent by in.mpathd.
1111 	 */
1112 	do {
1113 		infop = NULL;
1114 		retval = ipmp_readtlv(statep->st_fd, &type, &len, &infop, &end);
1115 		if (retval != IPMP_SUCCESS)
1116 			goto fail;
1117 
1118 		if (!ipmp_checktlv(type, len, infop)) {
1119 			retval = IPMP_EPROTO;
1120 			goto fail;
1121 		}
1122 
1123 		switch (type) {
1124 		case IPMP_GROUPLIST:
1125 			if (snap->sn_grlistp != NULL) {
1126 				retval = IPMP_EPROTO;
1127 				break;
1128 			}
1129 			snap->sn_grlistp = infop;
1130 			break;
1131 
1132 		case IPMP_IFINFO:
1133 			if (snap->sn_nif == nif) {
1134 				retval = IPMP_EPROTO;
1135 				break;
1136 			}
1137 
1138 			infop = ipmp_convertifinfo(infop);
1139 			if (infop == NULL) {
1140 				retval = IPMP_ENOMEM;
1141 				break;
1142 			}
1143 
1144 			/*
1145 			 * Read in V4 and V6 targlist TLVs that follow.
1146 			 */
1147 			retval = ipmp_readifinfo_lists(statep, infop, &end);
1148 			if (retval != IPMP_SUCCESS)
1149 				break;
1150 
1151 			retval = ipmp_snap_addifinfo(snap, infop);
1152 			if (retval != IPMP_SUCCESS) {
1153 				ipmp_freeifinfo(infop);
1154 				infop = NULL;
1155 			}
1156 			break;
1157 
1158 		case IPMP_ADDRINFO:
1159 			if (snap->sn_naddr == naddr) {
1160 				retval = IPMP_EPROTO;
1161 				break;
1162 			}
1163 
1164 			retval = ipmp_snap_addaddrinfo(snap, infop);
1165 			/*
1166 			 * NOTE: since we didn't call ipmp_read*info_lists(),
1167 			 * no need to use ipmp_freeaddrinfo() on failure.
1168 			 */
1169 			break;
1170 
1171 		case IPMP_GROUPINFO:
1172 			if (snap->sn_ngroup == ngroup) {
1173 				retval = IPMP_EPROTO;
1174 				break;
1175 			}
1176 
1177 			infop = ipmp_convertgroupinfo(infop);
1178 			if (infop == NULL) {
1179 				retval = IPMP_ENOMEM;
1180 				break;
1181 			}
1182 
1183 			/*
1184 			 * Read in IPMP groupinfo list TLVs that follow.
1185 			 */
1186 			retval = ipmp_readgroupinfo_lists(statep, infop, &end);
1187 			if (retval != IPMP_SUCCESS)
1188 				break;
1189 
1190 			retval = ipmp_snap_addgroupinfo(snap, infop);
1191 			if (retval != IPMP_SUCCESS) {
1192 				ipmp_freegroupinfo(infop);
1193 				infop = NULL;
1194 			}
1195 			break;
1196 
1197 		case IPMP_ADDRCNT:
1198 			if (naddr != UINT64_MAX) {
1199 				retval = IPMP_EPROTO;
1200 				break;
1201 			}
1202 
1203 			naddr = *(uint32_t *)infop;
1204 			break;
1205 
1206 		case IPMP_GROUPCNT:
1207 			if (ngroup != UINT64_MAX) {
1208 				retval = IPMP_EPROTO;
1209 				break;
1210 			}
1211 
1212 			ngroup = *(uint32_t *)infop;
1213 			break;
1214 
1215 		case IPMP_IFCNT:
1216 			if (nif != UINT64_MAX) {
1217 				retval = IPMP_EPROTO;
1218 				break;
1219 			}
1220 
1221 			nif = *(uint32_t *)infop;
1222 			break;
1223 
1224 		default:
1225 			retval = IPMP_EPROTO;
1226 			break;
1227 		}
1228 fail:
1229 		if (retval != IPMP_SUCCESS) {
1230 			free(infop);
1231 			ipmp_snap_free(snap);
1232 			return (ipmp_querydone(statep, retval));
1233 		}
1234 	} while (snap->sn_grlistp == NULL || snap->sn_nif < nif ||
1235 	    snap->sn_ngroup < ngroup || snap->sn_naddr < naddr);
1236 
1237 	*snapp = snap;
1238 	return (ipmp_querydone(statep, IPMP_SUCCESS));
1239 }
1240