| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| * |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* |
| * IPMP query interfaces (see PSARC/2002/615 and PSARC/2007/272). |
| */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <libinetutil.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| |
| #include "ipmp_impl.h" |
| #include "ipmp_mpathd.h" |
| #include "ipmp_query_impl.h" |
| |
| static ipmp_ifinfo_t *ipmp_ifinfo_clone(ipmp_ifinfo_t *); |
| static ipmp_addrinfo_t *ipmp_addrinfo_clone(ipmp_addrinfo_t *); |
| static ipmp_addrlist_t *ipmp_addrlist_clone(ipmp_addrlist_t *); |
| static ipmp_grouplist_t *ipmp_grouplist_clone(ipmp_grouplist_t *); |
| static ipmp_groupinfo_t *ipmp_groupinfo_clone(ipmp_groupinfo_t *); |
| static ipmp_iflist_t *ipmp_iflist_create(uint_t, char (*)[LIFNAMSIZ]); |
| static void ipmp_freeiflist(ipmp_iflist_t *); |
| static ipmp_addrlist_t *ipmp_addrlist_create(uint_t, struct sockaddr_storage *); |
| static void ipmp_freeaddrlist(ipmp_addrlist_t *); |
| static ipmp_groupinfo_t *ipmp_snap_getgroupinfo(ipmp_snap_t *, const char *); |
| static ipmp_ifinfo_t *ipmp_snap_getifinfo(ipmp_snap_t *, const char *); |
| static ipmp_addrinfo_t *ipmp_snap_getaddrinfo(ipmp_snap_t *, const char *, |
| struct sockaddr_storage *); |
| static int ipmp_snap_take(ipmp_state_t *, ipmp_snap_t **); |
| static boolean_t ipmp_checktlv(ipmp_infotype_t, size_t, void *); |
| static int ipmp_querydone(ipmp_state_t *, int); |
| |
| /* |
| * Using `statep', send a query request for `type' to in.mpathd, and if |
| * necessary wait until at least `endtp' for a response. Returns an IPMP |
| * error code. If successful, the caller may then read additional query |
| * information through ipmp_readinfo(), and must eventually call |
| * ipmp_querydone() to complete the query operation. Only one query may be |
| * outstanding on a given `statep' at a time. |
| */ |
| static int |
| ipmp_sendquery(ipmp_state_t *statep, ipmp_infotype_t type, const char *name, |
| const void *addr, struct timeval *endtp) |
| { |
| mi_query_t query; |
| mi_result_t result; |
| int retval; |
| |
| query.miq_command = MI_QUERY; |
| query.miq_inforeq = type; |
| |
| switch (type) { |
| case IPMP_ADDRINFO: |
| (void) strlcpy(query.miq_grname, name, LIFGRNAMSIZ); |
| query.miq_addr = *(struct sockaddr_storage *)addr; |
| break; |
| |
| case IPMP_GROUPINFO: |
| (void) strlcpy(query.miq_grname, name, LIFGRNAMSIZ); |
| break; |
| |
| case IPMP_IFINFO: |
| (void) strlcpy(query.miq_ifname, name, LIFNAMSIZ); |
| break; |
| |
| case IPMP_GROUPLIST: |
| case IPMP_SNAP: |
| break; |
| |
| default: |
| assert(0); |
| } |
| |
| if (gettimeofday(endtp, NULL) == -1) |
| return (IPMP_FAILURE); |
| |
| endtp->tv_sec += IPMP_REQTIMEOUT; |
| |
| assert(statep->st_fd == -1); |
| retval = ipmp_connect(&statep->st_fd); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| |
| retval = ipmp_write(statep->st_fd, &query, sizeof (query)); |
| if (retval != IPMP_SUCCESS) |
| return (ipmp_querydone(statep, retval)); |
| |
| retval = ipmp_read(statep->st_fd, &result, sizeof (result), endtp); |
| if (retval != IPMP_SUCCESS) |
| return (ipmp_querydone(statep, retval)); |
| |
| if (result.me_mpathd_error != IPMP_SUCCESS) |
| return (ipmp_querydone(statep, result.me_mpathd_error)); |
| |
| return (IPMP_SUCCESS); |
| } |
| |
| /* |
| * Using `statep', read a query response of type `infotype' into a dynamically |
| * allocated buffer pointed to by `*infop', before the current time becomes |
| * `endtp'. Returns an IPMP error code. |
| */ |
| static int |
| ipmp_readinfo(ipmp_state_t *statep, ipmp_infotype_t infotype, void **infop, |
| const struct timeval *endtp) |
| { |
| int retval; |
| size_t len; |
| ipmp_infotype_t type; |
| |
| retval = ipmp_readtlv(statep->st_fd, &type, &len, infop, endtp); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| |
| if (type != infotype || !ipmp_checktlv(type, len, *infop)) { |
| free(*infop); |
| return (IPMP_EPROTO); |
| } |
| |
| return (IPMP_SUCCESS); |
| } |
| |
| /* |
| * Using `statep', read in the remaining IPMP group information TLVs from |
| * in.mpathd into `grinfop' before the current time becomes `endtp'. Returns |
| * an IPMP error code. On failure, `grinfop' will have its original contents. |
| */ |
| static int |
| ipmp_readgroupinfo_lists(ipmp_state_t *statep, ipmp_groupinfo_t *grinfop, |
| const struct timeval *endtp) |
| { |
| int retval; |
| ipmp_iflist_t *iflistp; |
| ipmp_addrlist_t *adlistp; |
| |
| retval = ipmp_readinfo(statep, IPMP_IFLIST, (void **)&iflistp, endtp); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| |
| retval = ipmp_readinfo(statep, IPMP_ADDRLIST, (void **)&adlistp, endtp); |
| if (retval != IPMP_SUCCESS) { |
| ipmp_freeiflist(iflistp); |
| return (retval); |
| } |
| |
| grinfop->gr_iflistp = iflistp; |
| grinfop->gr_adlistp = adlistp; |
| return (IPMP_SUCCESS); |
| } |
| |
| /* |
| * Using `statep', read in the remaining IPMP interface information TLVs from |
| * in.mpathd into `ifinfop' before the current time becomes `endtp'. Returns |
| * an IPMP error code. On failure, `ifinfop' will have its original contents. |
| */ |
| static int |
| ipmp_readifinfo_lists(ipmp_state_t *statep, ipmp_ifinfo_t *ifinfop, |
| const struct timeval *endtp) |
| { |
| int retval; |
| ipmp_addrlist_t *tlist4p, *tlist6p; |
| |
| retval = ipmp_readinfo(statep, IPMP_ADDRLIST, (void **)&tlist4p, endtp); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| |
| retval = ipmp_readinfo(statep, IPMP_ADDRLIST, (void **)&tlist6p, endtp); |
| if (retval != IPMP_SUCCESS) { |
| ipmp_freeaddrlist(tlist4p); |
| return (retval); |
| } |
| |
| ifinfop->if_targinfo4.it_targlistp = tlist4p; |
| ifinfop->if_targinfo6.it_targlistp = tlist6p; |
| return (IPMP_SUCCESS); |
| } |
| |
| /* |
| * Complete the query operation started in ipmp_sendquery(). The interface is |
| * designed to be easy to use in the `return' statement of a function, and |
| * thus returns the passed in `retval' and preserves `errno'. |
| */ |
| static int |
| ipmp_querydone(ipmp_state_t *statep, int retval) |
| { |
| int error = errno; |
| |
| (void) close(statep->st_fd); |
| statep->st_fd = -1; |
| errno = error; |
| return (retval); |
| } |
| |
| /* |
| * Using `handle', get the group list and store the results in a dynamically |
| * allocated buffer pointed to by `*grlistpp'. Returns an IPMP error code. |
| */ |
| int |
| ipmp_getgrouplist(ipmp_handle_t handle, ipmp_grouplist_t **grlistpp) |
| { |
| ipmp_state_t *statep = handle; |
| struct timeval end; |
| int retval; |
| |
| if (statep->st_snap != NULL) { |
| *grlistpp = ipmp_grouplist_clone(statep->st_snap->sn_grlistp); |
| return (*grlistpp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM); |
| } |
| |
| retval = ipmp_sendquery(statep, IPMP_GROUPLIST, NULL, NULL, &end); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| |
| retval = ipmp_readinfo(statep, IPMP_GROUPLIST, (void **)grlistpp, &end); |
| return (ipmp_querydone(statep, retval)); |
| } |
| |
| /* |
| * Free the group list pointed to by `grlistp'. |
| */ |
| void |
| ipmp_freegrouplist(ipmp_grouplist_t *grlistp) |
| { |
| free(grlistp); |
| } |
| |
| /* |
| * Using `handle', get the group information associated with group `name' and |
| * store the results in a dynamically allocated buffer pointed to by |
| * `*grinfopp'. Returns an IPMP error code. |
| */ |
| int |
| ipmp_getgroupinfo(ipmp_handle_t handle, const char *name, |
| ipmp_groupinfo_t **grinfopp) |
| { |
| ipmp_state_t *statep = handle; |
| int retval; |
| struct timeval end; |
| ipmp_groupinfo_t *grinfop; |
| |
| if (statep->st_snap != NULL) { |
| grinfop = ipmp_snap_getgroupinfo(statep->st_snap, name); |
| if (grinfop == NULL) |
| return (IPMP_EUNKGROUP); |
| |
| *grinfopp = ipmp_groupinfo_clone(grinfop); |
| return (*grinfopp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM); |
| } |
| |
| retval = ipmp_sendquery(statep, IPMP_GROUPINFO, name, NULL, &end); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| |
| retval = ipmp_readinfo(statep, IPMP_GROUPINFO, (void **)grinfopp, &end); |
| if (retval != IPMP_SUCCESS) |
| return (ipmp_querydone(statep, retval)); |
| |
| retval = ipmp_readgroupinfo_lists(statep, *grinfopp, &end); |
| if (retval != IPMP_SUCCESS) |
| free(*grinfopp); |
| |
| return (ipmp_querydone(statep, retval)); |
| } |
| |
| /* |
| * Free the group information pointed to by `grinfop'. |
| */ |
| void |
| ipmp_freegroupinfo(ipmp_groupinfo_t *grinfop) |
| { |
| ipmp_freeaddrlist(grinfop->gr_adlistp); |
| ipmp_freeiflist(grinfop->gr_iflistp); |
| free(grinfop); |
| } |
| |
| /* |
| * Using `handle', get the interface information associated with interface |
| * `name' and store the results in a dynamically allocated buffer pointed to |
| * by `*ifinfopp'. Returns an IPMP error code. |
| */ |
| int |
| ipmp_getifinfo(ipmp_handle_t handle, const char *name, ipmp_ifinfo_t **ifinfopp) |
| { |
| ipmp_state_t *statep = handle; |
| ipmp_ifinfo_t *ifinfop; |
| int retval; |
| struct timeval end; |
| |
| if (statep->st_snap != NULL) { |
| ifinfop = ipmp_snap_getifinfo(statep->st_snap, name); |
| if (ifinfop == NULL) |
| return (IPMP_EUNKIF); |
| |
| *ifinfopp = ipmp_ifinfo_clone(ifinfop); |
| return (*ifinfopp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM); |
| } |
| |
| retval = ipmp_sendquery(statep, IPMP_IFINFO, name, NULL, &end); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| |
| retval = ipmp_readinfo(statep, IPMP_IFINFO, (void **)ifinfopp, &end); |
| if (retval != IPMP_SUCCESS) |
| return (ipmp_querydone(statep, retval)); |
| |
| retval = ipmp_readifinfo_lists(statep, *ifinfopp, &end); |
| if (retval != IPMP_SUCCESS) |
| free(*ifinfopp); |
| |
| return (ipmp_querydone(statep, retval)); |
| } |
| |
| /* |
| * Free the interface information pointed to by `ifinfop'. |
| */ |
| void |
| ipmp_freeifinfo(ipmp_ifinfo_t *ifinfop) |
| { |
| ipmp_freeaddrlist(ifinfop->if_targinfo4.it_targlistp); |
| ipmp_freeaddrlist(ifinfop->if_targinfo6.it_targlistp); |
| free(ifinfop); |
| } |
| |
| /* |
| * Using `handle', get the address information associated with address `addrp' |
| * on group `grname' and store the results in a dynamically allocated buffer |
| * pointed to by `*adinfopp'. Returns an IPMP error code. |
| */ |
| int |
| ipmp_getaddrinfo(ipmp_handle_t handle, const char *grname, |
| struct sockaddr_storage *addrp, ipmp_addrinfo_t **adinfopp) |
| { |
| ipmp_state_t *statep = handle; |
| ipmp_addrinfo_t *adinfop; |
| int retval; |
| struct timeval end; |
| |
| if (statep->st_snap != NULL) { |
| adinfop = ipmp_snap_getaddrinfo(statep->st_snap, grname, addrp); |
| if (adinfop == NULL) |
| return (IPMP_EUNKADDR); |
| |
| *adinfopp = ipmp_addrinfo_clone(adinfop); |
| return (*adinfopp != NULL ? IPMP_SUCCESS : IPMP_ENOMEM); |
| } |
| |
| retval = ipmp_sendquery(statep, IPMP_ADDRINFO, grname, addrp, &end); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| |
| retval = ipmp_readinfo(statep, IPMP_ADDRINFO, (void **)adinfopp, &end); |
| return (ipmp_querydone(statep, retval)); |
| } |
| |
| /* |
| * Free the address information pointed to by `adinfop'. |
| */ |
| void |
| ipmp_freeaddrinfo(ipmp_addrinfo_t *adinfop) |
| { |
| free(adinfop); |
| } |
| |
| /* |
| * Check if `buf' has a NUL byte in its first `bufsize' bytes. |
| */ |
| static boolean_t |
| hasnulbyte(const char *buf, size_t bufsize) |
| { |
| while (bufsize-- > 0) { |
| if (buf[bufsize] == '\0') |
| return (B_TRUE); |
| } |
| return (B_FALSE); |
| } |
| |
| /* |
| * Check that the TLV triplet named by `type', `len' and `value' is correctly |
| * formed. |
| */ |
| static boolean_t |
| ipmp_checktlv(ipmp_infotype_t type, size_t len, void *value) |
| { |
| ipmp_iflist_t *iflistp; |
| ipmp_ifinfo_t *ifinfop; |
| ipmp_grouplist_t *grlistp; |
| ipmp_groupinfo_t *grinfop; |
| ipmp_addrlist_t *adlistp; |
| unsigned int i; |
| |
| switch (type) { |
| case IPMP_ADDRINFO: |
| if (len != sizeof (ipmp_addrinfo_t)) |
| return (B_FALSE); |
| break; |
| |
| case IPMP_ADDRLIST: |
| adlistp = (ipmp_addrlist_t *)value; |
| if (len < IPMP_ADDRLIST_SIZE(0) || |
| len < IPMP_ADDRLIST_SIZE(adlistp->al_naddr)) |
| return (B_FALSE); |
| break; |
| |
| case IPMP_IFLIST: |
| iflistp = (ipmp_iflist_t *)value; |
| if (len < IPMP_IFLIST_SIZE(0) || |
| len < IPMP_IFLIST_SIZE(iflistp->il_nif)) |
| return (B_FALSE); |
| |
| for (i = 0; i < iflistp->il_nif; i++) |
| if (!hasnulbyte(iflistp->il_ifs[i], LIFNAMSIZ)) |
| return (B_FALSE); |
| break; |
| |
| case IPMP_IFINFO: |
| ifinfop = (ipmp_ifinfo_t *)value; |
| if (len != sizeof (ipmp_ifinfo_t)) |
| return (B_FALSE); |
| |
| if (!hasnulbyte(ifinfop->if_name, LIFNAMSIZ) || |
| !hasnulbyte(ifinfop->if_group, LIFGRNAMSIZ)) |
| return (B_FALSE); |
| break; |
| |
| case IPMP_GROUPLIST: |
| grlistp = (ipmp_grouplist_t *)value; |
| if (len < IPMP_GROUPLIST_SIZE(0) || |
| len < IPMP_GROUPLIST_SIZE(grlistp->gl_ngroup)) |
| return (B_FALSE); |
| |
| for (i = 0; i < grlistp->gl_ngroup; i++) |
| if (!hasnulbyte(grlistp->gl_groups[i], LIFGRNAMSIZ)) |
| return (B_FALSE); |
| break; |
| |
| case IPMP_GROUPINFO: |
| grinfop = (ipmp_groupinfo_t *)value; |
| if (len != sizeof (ipmp_groupinfo_t)) |
| return (B_FALSE); |
| |
| if (!hasnulbyte(grinfop->gr_name, LIFGRNAMSIZ)) |
| return (B_FALSE); |
| break; |
| |
| case IPMP_SNAP: |
| if (len != sizeof (ipmp_snap_t)) |
| return (B_FALSE); |
| break; |
| |
| default: |
| return (B_FALSE); |
| } |
| |
| return (B_TRUE); |
| } |
| |
| /* |
| * Create a group list; arguments match ipmp_grouplist_t fields. Returns a |
| * pointer to the new group list on success, or NULL on failure. |
| */ |
| ipmp_grouplist_t * |
| ipmp_grouplist_create(uint64_t sig, unsigned int ngroup, |
| char (*groups)[LIFGRNAMSIZ]) |
| { |
| unsigned int i; |
| ipmp_grouplist_t *grlistp; |
| |
| grlistp = malloc(IPMP_GROUPLIST_SIZE(ngroup)); |
| if (grlistp == NULL) |
| return (NULL); |
| |
| grlistp->gl_sig = sig; |
| grlistp->gl_ngroup = ngroup; |
| for (i = 0; i < ngroup; i++) |
| (void) strlcpy(grlistp->gl_groups[i], groups[i], LIFGRNAMSIZ); |
| |
| return (grlistp); |
| } |
| |
| /* |
| * Clone the group list named by `grlistp'. Returns a pointer to the clone on |
| * success, or NULL on failure. |
| */ |
| ipmp_grouplist_t * |
| ipmp_grouplist_clone(ipmp_grouplist_t *grlistp) |
| { |
| return (ipmp_grouplist_create(grlistp->gl_sig, grlistp->gl_ngroup, |
| grlistp->gl_groups)); |
| } |
| |
| /* |
| * Create target information; arguments match ipmp_targinfo_t fields. Returns |
| * a pointer to the new target info on success, or NULL on failure. |
| */ |
| 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) |
| { |
| ipmp_targinfo_t *targinfop; |
| |
| targinfop = malloc(sizeof (ipmp_targinfo_t)); |
| if (targinfop == NULL) |
| return (NULL); |
| |
| targinfop->it_testaddr = *testaddrp; |
| targinfop->it_targmode = targmode; |
| targinfop->it_targlistp = ipmp_addrlist_create(ntarg, targs); |
| if (targinfop->it_targlistp == NULL) { |
| ipmp_freetarginfo(targinfop); |
| return (NULL); |
| } |
| (void) strlcpy(targinfop->it_name, name, LIFNAMSIZ); |
| |
| return (targinfop); |
| } |
| |
| /* |
| * Free the target information pointed to by `targinfop'. |
| */ |
| void |
| ipmp_freetarginfo(ipmp_targinfo_t *targinfop) |
| { |
| free(targinfop->it_targlistp); |
| free(targinfop); |
| } |
| |
| /* |
| * Create an interface list; arguments match ipmp_iflist_t fields. Returns a |
| * pointer to the new interface list on success, or NULL on failure. |
| */ |
| static ipmp_iflist_t * |
| ipmp_iflist_create(uint_t nif, char (*ifs)[LIFNAMSIZ]) |
| { |
| unsigned int i; |
| ipmp_iflist_t *iflistp; |
| |
| iflistp = malloc(IPMP_IFLIST_SIZE(nif)); |
| if (iflistp == NULL) |
| return (NULL); |
| |
| iflistp->il_nif = nif; |
| for (i = 0; i < nif; i++) |
| (void) strlcpy(iflistp->il_ifs[i], ifs[i], LIFNAMSIZ); |
| |
| return (iflistp); |
| } |
| |
| /* |
| * Free the interface list pointed to by `iflistp'. |
| */ |
| static void |
| ipmp_freeiflist(ipmp_iflist_t *iflistp) |
| { |
| free(iflistp); |
| } |
| |
| /* |
| * Create an interface; arguments match ipmp_ifinfo_t fields. Returns a |
| * pointer to the new interface on success, or NULL on failure. |
| */ |
| 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) |
| { |
| ipmp_ifinfo_t *ifinfop; |
| |
| ifinfop = malloc(sizeof (ipmp_ifinfo_t)); |
| if (ifinfop == NULL) |
| return (NULL); |
| |
| (void) strlcpy(ifinfop->if_name, name, LIFNAMSIZ); |
| (void) strlcpy(ifinfop->if_group, group, LIFGRNAMSIZ); |
| |
| ifinfop->if_state = state; |
| ifinfop->if_type = type; |
| ifinfop->if_linkstate = linkstate; |
| ifinfop->if_probestate = probestate; |
| ifinfop->if_flags = flags; |
| ifinfop->if_targinfo4 = *targinfo4p; |
| ifinfop->if_targinfo6 = *targinfo6p; |
| |
| ifinfop->if_targinfo4.it_targlistp = |
| ipmp_addrlist_clone(targinfo4p->it_targlistp); |
| ifinfop->if_targinfo6.it_targlistp = |
| ipmp_addrlist_clone(targinfo6p->it_targlistp); |
| |
| if (ifinfop->if_targinfo4.it_targlistp == NULL || |
| ifinfop->if_targinfo6.it_targlistp == NULL) { |
| ipmp_freeifinfo(ifinfop); |
| return (NULL); |
| } |
| |
| return (ifinfop); |
| } |
| |
| /* |
| * Clone the interface information named by `ifinfop'. Returns a pointer to |
| * the clone on success, or NULL on failure. |
| */ |
| ipmp_ifinfo_t * |
| ipmp_ifinfo_clone(ipmp_ifinfo_t *ifinfop) |
| { |
| return (ipmp_ifinfo_create(ifinfop->if_name, ifinfop->if_group, |
| ifinfop->if_state, ifinfop->if_type, ifinfop->if_linkstate, |
| ifinfop->if_probestate, ifinfop->if_flags, &ifinfop->if_targinfo4, |
| &ifinfop->if_targinfo6)); |
| } |
| |
| /* |
| * Create a group; arguments match ipmp_groupinfo_t fields. Returns a pointer |
| * to the new group on success, or NULL on failure. |
| */ |
| 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) |
| { |
| ipmp_groupinfo_t *grinfop; |
| |
| grinfop = malloc(sizeof (ipmp_groupinfo_t)); |
| if (grinfop == NULL) |
| return (NULL); |
| |
| grinfop->gr_sig = sig; |
| grinfop->gr_fdt = fdt; |
| grinfop->gr_state = state; |
| grinfop->gr_iflistp = ipmp_iflist_create(nif, ifs); |
| grinfop->gr_adlistp = ipmp_addrlist_create(naddr, addrs); |
| if (grinfop->gr_iflistp == NULL || grinfop->gr_adlistp == NULL) { |
| ipmp_freegroupinfo(grinfop); |
| return (NULL); |
| } |
| (void) strlcpy(grinfop->gr_name, name, LIFGRNAMSIZ); |
| (void) strlcpy(grinfop->gr_ifname, grifname, LIFNAMSIZ); |
| (void) strlcpy(grinfop->gr_m4ifname, m4ifname, LIFNAMSIZ); |
| (void) strlcpy(grinfop->gr_m6ifname, m6ifname, LIFNAMSIZ); |
| (void) strlcpy(grinfop->gr_bcifname, bcifname, LIFNAMSIZ); |
| |
| return (grinfop); |
| } |
| |
| /* |
| * Clone the group information named by `grinfop'. Returns a pointer to |
| * the clone on success, or NULL on failure. |
| */ |
| ipmp_groupinfo_t * |
| ipmp_groupinfo_clone(ipmp_groupinfo_t *grinfop) |
| { |
| ipmp_addrlist_t *adlistp = grinfop->gr_adlistp; |
| |
| return (ipmp_groupinfo_create(grinfop->gr_name, grinfop->gr_sig, |
| grinfop->gr_fdt, grinfop->gr_state, grinfop->gr_iflistp->il_nif, |
| grinfop->gr_iflistp->il_ifs, grinfop->gr_ifname, |
| grinfop->gr_m4ifname, grinfop->gr_m6ifname, grinfop->gr_bcifname, |
| adlistp->al_naddr, adlistp->al_addrs)); |
| } |
| |
| /* |
| * Create an address list; arguments match ipmp_addrlist_t fields. Returns |
| * a pointer to the new address list on success, or NULL on failure. |
| */ |
| static ipmp_addrlist_t * |
| ipmp_addrlist_create(uint_t naddr, struct sockaddr_storage *addrs) |
| { |
| unsigned int i; |
| ipmp_addrlist_t *adlistp; |
| |
| adlistp = malloc(IPMP_ADDRLIST_SIZE(naddr)); |
| if (adlistp == NULL) |
| return (NULL); |
| |
| adlistp->al_naddr = naddr; |
| for (i = 0; i < naddr; i++) |
| adlistp->al_addrs[i] = addrs[i]; |
| |
| return (adlistp); |
| } |
| |
| /* |
| * Clone the address list named by `adlistp'. Returns a pointer to the clone |
| * on success, or NULL on failure. |
| */ |
| static ipmp_addrlist_t * |
| ipmp_addrlist_clone(ipmp_addrlist_t *adlistp) |
| { |
| return (ipmp_addrlist_create(adlistp->al_naddr, adlistp->al_addrs)); |
| } |
| |
| /* |
| * Free the address list pointed to by `adlistp'. |
| */ |
| static void |
| ipmp_freeaddrlist(ipmp_addrlist_t *adlistp) |
| { |
| free(adlistp); |
| } |
| |
| /* |
| * Create an address; arguments match ipmp_addrinfo_t fields. Returns a |
| * pointer to the new address on success, or NULL on failure. |
| */ |
| ipmp_addrinfo_t * |
| ipmp_addrinfo_create(struct sockaddr_storage *addrp, ipmp_addr_state_t state, |
| const char *group, const char *binding) |
| { |
| ipmp_addrinfo_t *adinfop; |
| |
| adinfop = malloc(sizeof (ipmp_addrinfo_t)); |
| if (adinfop == NULL) |
| return (NULL); |
| |
| adinfop->ad_addr = *addrp; |
| adinfop->ad_state = state; |
| (void) strlcpy(adinfop->ad_group, group, LIFGRNAMSIZ); |
| (void) strlcpy(adinfop->ad_binding, binding, LIFNAMSIZ); |
| |
| return (adinfop); |
| } |
| |
| /* |
| * Clone the address information named by `adinfop'. Returns a pointer to |
| * the clone on success, or NULL on failure. |
| */ |
| ipmp_addrinfo_t * |
| ipmp_addrinfo_clone(ipmp_addrinfo_t *adinfop) |
| { |
| return (ipmp_addrinfo_create(&adinfop->ad_addr, adinfop->ad_state, |
| adinfop->ad_group, adinfop->ad_binding)); |
| } |
| |
| /* |
| * Set the query context associated with `handle' to `qcontext', which must be |
| * either IPMP_QCONTEXT_LIVE or IPMP_QCONTEXT_SNAP. Upon success, any |
| * previous snapshot associated with `handle' is discarded. Returns an IPMP |
| * error code. |
| */ |
| int |
| ipmp_setqcontext(ipmp_handle_t handle, ipmp_qcontext_t qcontext) |
| { |
| ipmp_state_t *statep = handle; |
| ipmp_snap_t *snap; |
| int retval; |
| |
| switch (qcontext) { |
| case IPMP_QCONTEXT_LIVE: |
| snap = NULL; |
| break; |
| |
| case IPMP_QCONTEXT_SNAP: |
| retval = ipmp_snap_take(statep, &snap); |
| if (retval != IPMP_SUCCESS) |
| return (retval); |
| break; |
| |
| default: |
| return (IPMP_EINVAL); |
| } |
| |
| if (statep->st_snap != NULL) |
| ipmp_snap_free(statep->st_snap); |
| statep->st_snap = snap; |
| |
| return (IPMP_SUCCESS); |
| } |
| |
| /* |
| * Create an empty snapshot. Returns a pointer to the snapshot on success, |
| * or NULL on failure. |
| */ |
| ipmp_snap_t * |
| ipmp_snap_create(void) |
| { |
| ipmp_snap_t *snap; |
| |
| snap = malloc(sizeof (ipmp_snap_t)); |
| if (snap == NULL) |
| return (NULL); |
| |
| snap->sn_grlistp = NULL; |
| snap->sn_grinfolistp = NULL; |
| snap->sn_ifinfolistp = NULL; |
| snap->sn_adinfolistp = NULL; |
| snap->sn_ngroup = 0; |
| snap->sn_nif = 0; |
| snap->sn_naddr = 0; |
| |
| return (snap); |
| } |
| |
| /* |
| * Free all of the resources associated with snapshot `snap'. |
| */ |
| void |
| ipmp_snap_free(ipmp_snap_t *snap) |
| { |
| ipmp_ifinfolist_t *iflp, *ifnext; |
| ipmp_addrinfolist_t *adlp, *adnext; |
| ipmp_groupinfolist_t *grlp, *grnext; |
| |
| ipmp_freegrouplist(snap->sn_grlistp); |
| |
| for (grlp = snap->sn_grinfolistp; grlp != NULL; grlp = grnext) { |
| grnext = grlp->grl_next; |
| ipmp_freegroupinfo(grlp->grl_grinfop); |
| free(grlp); |
| } |
| |
| for (iflp = snap->sn_ifinfolistp; iflp != NULL; iflp = ifnext) { |
| ifnext = iflp->ifl_next; |
| ipmp_freeifinfo(iflp->ifl_ifinfop); |
| free(iflp); |
| } |
| |
| for (adlp = snap->sn_adinfolistp; adlp != NULL; adlp = adnext) { |
| adnext = adlp->adl_next; |
| ipmp_freeaddrinfo(adlp->adl_adinfop); |
| free(adlp); |
| } |
| |
| free(snap); |
| } |
| |
| /* |
| * Add the group information in `grinfop' to the snapshot named by `snap'. |
| * Returns an IPMP error code. |
| */ |
| int |
| ipmp_snap_addgroupinfo(ipmp_snap_t *snap, ipmp_groupinfo_t *grinfop) |
| { |
| ipmp_groupinfolist_t *grlp; |
| |
| /* |
| * If the information for this group is already in the snapshot, |
| * in.mpathd is broken. |
| */ |
| if (ipmp_snap_getgroupinfo(snap, grinfop->gr_name) != NULL) |
| return (IPMP_EPROTO); |
| |
| grlp = malloc(sizeof (ipmp_groupinfolist_t)); |
| if (grlp == NULL) |
| return (IPMP_ENOMEM); |
| |
| grlp->grl_grinfop = grinfop; |
| grlp->grl_next = snap->sn_grinfolistp; |
| snap->sn_grinfolistp = grlp; |
| snap->sn_ngroup++; |
| |
| return (IPMP_SUCCESS); |
| } |
| |
| /* |
| * Add the interface information in `ifinfop' to the snapshot named by `snap'. |
| * Returns an IPMP error code. |
| */ |
| int |
| ipmp_snap_addifinfo(ipmp_snap_t *snap, ipmp_ifinfo_t *ifinfop) |
| { |
| ipmp_ifinfolist_t *iflp; |
| |
| /* |
| * If the information for this interface is already in the snapshot, |
| * in.mpathd is broken. |
| */ |
| if (ipmp_snap_getifinfo(snap, ifinfop->if_name) != NULL) |
| return (IPMP_EPROTO); |
| |
| iflp = malloc(sizeof (ipmp_ifinfolist_t)); |
| if (iflp == NULL) |
| return (IPMP_ENOMEM); |
| |
| iflp->ifl_ifinfop = ifinfop; |
| iflp->ifl_next = snap->sn_ifinfolistp; |
| snap->sn_ifinfolistp = iflp; |
| snap->sn_nif++; |
| |
| return (IPMP_SUCCESS); |
| } |
| |
| /* |
| * Add the address information in `adinfop' to the snapshot named by `snap'. |
| * Returns an IPMP error code. |
| */ |
| int |
| ipmp_snap_addaddrinfo(ipmp_snap_t *snap, ipmp_addrinfo_t *adinfop) |
| { |
| ipmp_addrinfolist_t *adlp; |
| |
| /* |
| * Any duplicate addresses should've already been weeded by in.mpathd. |
| */ |
| if (ipmp_snap_getaddrinfo(snap, adinfop->ad_group, |
| &adinfop->ad_addr) != NULL) |
| return (IPMP_EPROTO); |
| |
| adlp = malloc(sizeof (ipmp_addrinfolist_t)); |
| if (adlp == NULL) |
| return (IPMP_ENOMEM); |
| |
| adlp->adl_adinfop = adinfop; |
| adlp->adl_next = snap->sn_adinfolistp; |
| snap->sn_adinfolistp = adlp; |
| snap->sn_naddr++; |
| |
| return (IPMP_SUCCESS); |
| } |
| |
| /* |
| * Retrieve the information for the group `name' in snapshot `snap'. |
| * Returns a pointer to the group information on success, or NULL on failure. |
| */ |
| static ipmp_groupinfo_t * |
| ipmp_snap_getgroupinfo(ipmp_snap_t *snap, const char *name) |
| { |
| ipmp_groupinfolist_t *grlp; |
| |
| for (grlp = snap->sn_grinfolistp; grlp != NULL; grlp = grlp->grl_next) { |
| if (strcmp(grlp->grl_grinfop->gr_name, name) == 0) |
| break; |
| } |
| |
| return (grlp != NULL ? grlp->grl_grinfop : NULL); |
| } |
| |
| /* |
| * Retrieve the information for the interface `name' in snapshot `snap'. |
| * Returns a pointer to the interface information on success, or NULL on |
| * failure. |
| */ |
| static ipmp_ifinfo_t * |
| ipmp_snap_getifinfo(ipmp_snap_t *snap, const char *name) |
| { |
| ipmp_ifinfolist_t *iflp; |
| |
| for (iflp = snap->sn_ifinfolistp; iflp != NULL; iflp = iflp->ifl_next) { |
| if (strcmp(iflp->ifl_ifinfop->if_name, name) == 0) |
| break; |
| } |
| |
| return (iflp != NULL ? iflp->ifl_ifinfop : NULL); |
| } |
| |
| /* |
| * Retrieve the information for the address `addrp' on group `grname' in |
| * snapshot `snap'. Returns a pointer to the address information on success, |
| * or NULL on failure. |
| */ |
| static ipmp_addrinfo_t * |
| ipmp_snap_getaddrinfo(ipmp_snap_t *snap, const char *grname, |
| struct sockaddr_storage *addrp) |
| { |
| ipmp_addrinfolist_t *adlp; |
| |
| for (adlp = snap->sn_adinfolistp; adlp != NULL; adlp = adlp->adl_next) { |
| if (strcmp(grname, adlp->adl_adinfop->ad_group) == 0 && |
| sockaddrcmp(addrp, &adlp->adl_adinfop->ad_addr)) |
| break; |
| } |
| |
| return (adlp != NULL ? adlp->adl_adinfop : NULL); |
| } |
| |
| /* |
| * Using `statep', take a snapshot of the IPMP subsystem and if successful |
| * return it in a dynamically allocated snapshot pointed to by `*snapp'. |
| * Returns an IPMP error code. |
| */ |
| static int |
| ipmp_snap_take(ipmp_state_t *statep, ipmp_snap_t **snapp) |
| { |
| ipmp_snap_t *snap, *osnap; |
| ipmp_infotype_t type; |
| int retval; |
| size_t len; |
| void *infop; |
| struct timeval end; |
| |
| snap = ipmp_snap_create(); |
| if (snap == NULL) |
| return (IPMP_ENOMEM); |
| |
| retval = ipmp_sendquery(statep, IPMP_SNAP, NULL, NULL, &end); |
| if (retval != IPMP_SUCCESS) { |
| ipmp_snap_free(snap); |
| return (retval); |
| } |
| |
| retval = ipmp_readinfo(statep, IPMP_SNAP, (void **)&osnap, &end); |
| if (retval != IPMP_SUCCESS) { |
| ipmp_snap_free(snap); |
| return (ipmp_querydone(statep, retval)); |
| } |
| |
| /* |
| * Using the information in the `osnap' snapshot, build up our own |
| * snapshot. We know there will always be at least one TLV (for |
| * IPMP_GROUPLIST). If we receive anything illogical (e.g., more than |
| * the expected number of interfaces), then bail out. However, to a |
| * large extent we have to trust the information sent by in.mpathd. |
| */ |
| do { |
| infop = NULL; |
| retval = ipmp_readtlv(statep->st_fd, &type, &len, &infop, &end); |
| if (retval != IPMP_SUCCESS) |
| goto fail; |
| |
| if (!ipmp_checktlv(type, len, infop)) { |
| retval = IPMP_EPROTO; |
| goto fail; |
| } |
| |
| switch (type) { |
| case IPMP_GROUPLIST: |
| if (snap->sn_grlistp != NULL) { |
| retval = IPMP_EPROTO; |
| break; |
| } |
| snap->sn_grlistp = infop; |
| break; |
| |
| case IPMP_IFINFO: |
| if (snap->sn_nif == osnap->sn_nif) { |
| retval = IPMP_EPROTO; |
| break; |
| } |
| |
| /* |
| * Read in V4 and V6 targlist TLVs that follow. |
| */ |
| retval = ipmp_readifinfo_lists(statep, infop, &end); |
| if (retval != IPMP_SUCCESS) |
| break; |
| |
| retval = ipmp_snap_addifinfo(snap, infop); |
| if (retval != IPMP_SUCCESS) { |
| ipmp_freeifinfo(infop); |
| infop = NULL; |
| } |
| break; |
| |
| case IPMP_ADDRINFO: |
| if (snap->sn_naddr == osnap->sn_naddr) { |
| retval = IPMP_EPROTO; |
| break; |
| } |
| |
| retval = ipmp_snap_addaddrinfo(snap, infop); |
| /* |
| * NOTE: since we didn't call ipmp_read*info_lists(), |
| * no need to use ipmp_freeaddrinfo() on failure. |
| */ |
| break; |
| |
| case IPMP_GROUPINFO: |
| if (snap->sn_ngroup == osnap->sn_ngroup) { |
| retval = IPMP_EPROTO; |
| break; |
| } |
| |
| /* |
| * Read in IPMP groupinfo list TLVs that follow. |
| */ |
| retval = ipmp_readgroupinfo_lists(statep, infop, &end); |
| if (retval != IPMP_SUCCESS) |
| break; |
| |
| retval = ipmp_snap_addgroupinfo(snap, infop); |
| if (retval != IPMP_SUCCESS) { |
| ipmp_freegroupinfo(infop); |
| infop = NULL; |
| } |
| break; |
| |
| default: |
| retval = IPMP_EPROTO; |
| break; |
| } |
| fail: |
| if (retval != IPMP_SUCCESS) { |
| free(infop); |
| free(osnap); |
| ipmp_snap_free(snap); |
| return (ipmp_querydone(statep, retval)); |
| } |
| } while (snap->sn_grlistp == NULL || snap->sn_nif < osnap->sn_nif || |
| snap->sn_ngroup < osnap->sn_ngroup || |
| snap->sn_naddr < osnap->sn_naddr); |
| |
| free(osnap); |
| *snapp = snap; |
| return (ipmp_querydone(statep, IPMP_SUCCESS)); |
| } |