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