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