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
ipmp_sendquery(ipmp_state_t * statep,ipmp_infotype_t type,const char * name,const void * addr,struct timeval * endtp)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
ipmp_readinfo(ipmp_state_t * statep,ipmp_infotype_t infotype,void ** infop,const struct timeval * endtp)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
ipmp_readgroupinfo_lists(ipmp_state_t * statep,ipmp_groupinfo_t * grinfop,const struct timeval * endtp)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
ipmp_readifinfo_lists(ipmp_state_t * statep,ipmp_ifinfo_t * ifinfop,const struct timeval * endtp)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
ipmp_querydone(ipmp_state_t * statep,int retval)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
ipmp_getgrouplist(ipmp_handle_t handle,ipmp_grouplist_t ** grlistpp)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
ipmp_freegrouplist(ipmp_grouplist_t * grlistp)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 *
ipmp_convertgroupinfo(ipmp_groupinfo_xfer_t * grxferp)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
ipmp_getgroupinfo(ipmp_handle_t handle,const char * name,ipmp_groupinfo_t ** grinfopp)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
ipmp_freegroupinfo(ipmp_groupinfo_t * grinfop)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 *
ipmp_convertifinfo(ipmp_ifinfo_xfer_t * ifxferp)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
ipmp_getifinfo(ipmp_handle_t handle,const char * name,ipmp_ifinfo_t ** ifinfopp)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
ipmp_freeifinfo(ipmp_ifinfo_t * ifinfop)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
ipmp_getaddrinfo(ipmp_handle_t handle,const char * grname,struct sockaddr_storage * addrp,ipmp_addrinfo_t ** adinfopp)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
ipmp_freeaddrinfo(ipmp_addrinfo_t * adinfop)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
hasnulbyte(const char * buf,size_t bufsize)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
ipmp_checktlv(ipmp_infotype_t type,size_t len,void * value)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 *
ipmp_grouplist_create(uint64_t sig,unsigned int ngroup,char (* groups)[LIFGRNAMSIZ])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 *
ipmp_grouplist_clone(ipmp_grouplist_t * grlistp)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 *
ipmp_targinfo_create(const char * name,struct sockaddr_storage * testaddrp,ipmp_if_targmode_t targmode,uint_t ntarg,struct sockaddr_storage * targs)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
ipmp_freetarginfo(ipmp_targinfo_t * targinfop)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 *
ipmp_iflist_create(uint_t nif,char (* ifs)[LIFNAMSIZ])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
ipmp_freeiflist(ipmp_iflist_t * iflistp)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 *
ipmp_ifinfo_create(const char * name,const char * group,ipmp_if_state_t state,ipmp_if_type_t type,ipmp_if_linkstate_t linkstate,ipmp_if_probestate_t probestate,ipmp_if_flags_t flags,ipmp_targinfo_t * targinfo4p,ipmp_targinfo_t * targinfo6p)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 *
ipmp_ifinfo_clone(ipmp_ifinfo_t * ifinfop)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 *
ipmp_groupinfo_create(const char * name,uint64_t sig,uint_t fdt,ipmp_group_state_t state,uint_t nif,char (* ifs)[LIFNAMSIZ],const char * grifname,const char * m4ifname,const char * m6ifname,const char * bcifname,uint_t naddr,struct sockaddr_storage * addrs)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 *
ipmp_groupinfo_clone(ipmp_groupinfo_t * grinfop)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 *
ipmp_addrlist_create(uint_t naddr,struct sockaddr_storage * addrs)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 *
ipmp_addrlist_clone(ipmp_addrlist_t * adlistp)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
ipmp_freeaddrlist(ipmp_addrlist_t * adlistp)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 *
ipmp_addrinfo_create(struct sockaddr_storage * addrp,ipmp_addr_state_t state,const char * group,const char * binding)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 *
ipmp_addrinfo_clone(ipmp_addrinfo_t * adinfop)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
ipmp_setqcontext(ipmp_handle_t handle,ipmp_qcontext_t qcontext)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 *
ipmp_snap_create(void)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
ipmp_snap_free(ipmp_snap_t * snap)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
ipmp_snap_addgroupinfo(ipmp_snap_t * snap,ipmp_groupinfo_t * grinfop)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
ipmp_snap_addifinfo(ipmp_snap_t * snap,ipmp_ifinfo_t * ifinfop)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
ipmp_snap_addaddrinfo(ipmp_snap_t * snap,ipmp_addrinfo_t * adinfop)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 *
ipmp_snap_getgroupinfo(ipmp_snap_t * snap,const char * name)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 *
ipmp_snap_getifinfo(ipmp_snap_t * snap,const char * name)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 *
ipmp_snap_getaddrinfo(ipmp_snap_t * snap,const char * grname,struct sockaddr_storage * addrp)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
ipmp_snap_take(ipmp_state_t * statep,ipmp_snap_t ** snapp)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