/* * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include extern char cmlog[]; extern int ibcm_resolver_pr_lookup(ibcm_arp_streams_t *ib_s, ibt_ip_addr_t *dst_addr, ibt_ip_addr_t *src_addr); extern void ibcm_arp_delete_prwqn(ibcm_arp_prwqn_t *wqnp); _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibt_ip_addr_s)) _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_ip_t)) _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_ibd_insts_t)) _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_prwqn_t)) _NOTE(SCHEME_PROTECTS_DATA("Unshared data", sockaddr_in)) _NOTE(SCHEME_PROTECTS_DATA("Unshared data", sockaddr_in6)) int ibcm_printip = 0; /* * Function: * ibcm_ip_print * Input: * label Arbitrary qualifying string * ipa Pointer to IP Address to print */ void ibcm_ip_print(char *label, ibt_ip_addr_t *ipaddr) { char buf[INET6_ADDRSTRLEN]; if (ipaddr->family == AF_INET) { IBTF_DPRINTF_L2(cmlog, "%s: %s", label, inet_ntop(AF_INET, &ipaddr->un.ip4addr, buf, sizeof (buf))); } else if (ipaddr->family == AF_INET6) { IBTF_DPRINTF_L2(cmlog, "%s: %s", label, inet_ntop(AF_INET6, &ipaddr->un.ip6addr, buf, sizeof (buf))); } else { IBTF_DPRINTF_L2(cmlog, "%s: IP ADDR NOT SPECIFIED ", label); } } ibt_status_t ibcm_arp_get_ibaddr(ibt_ip_addr_t srcaddr, ibt_ip_addr_t destaddr, ib_gid_t *sgid, ib_gid_t *dgid) { ibcm_arp_streams_t *ib_s; ibcm_arp_prwqn_t *wqnp; int ret = 0; IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibaddr(%p, %p, %p, %p)", srcaddr, destaddr, sgid, dgid); ib_s = (ibcm_arp_streams_t *)kmem_zalloc(sizeof (ibcm_arp_streams_t), KM_SLEEP); mutex_init(&ib_s->lock, NULL, MUTEX_DEFAULT, NULL); cv_init(&ib_s->cv, NULL, CV_DRIVER, NULL); mutex_enter(&ib_s->lock); ib_s->done = B_FALSE; mutex_exit(&ib_s->lock); ret = ibcm_resolver_pr_lookup(ib_s, &destaddr, &srcaddr); IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_ibaddr: ibcm_resolver_pr_lookup " "returned: %d", ret); if (ret == 0) { mutex_enter(&ib_s->lock); while (ib_s->done != B_TRUE) cv_wait(&ib_s->cv, &ib_s->lock); mutex_exit(&ib_s->lock); } mutex_enter(&ib_s->lock); wqnp = ib_s->wqnp; if (ib_s->status == 0) { if (sgid) *sgid = ib_s->wqnp->sgid; if (dgid) *dgid = ib_s->wqnp->dgid; IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibaddr: SGID: %llX:%llX" " DGID: %llX:%llX", ib_s->wqnp->sgid.gid_prefix, ib_s->wqnp->sgid.gid_guid, ib_s->wqnp->dgid.gid_prefix, ib_s->wqnp->dgid.gid_guid); ibcm_arp_delete_prwqn(wqnp); } else if (ret == 0) { /* * We come here only when lookup has returned empty (failed) * via callback routine. * i.e. ib_s->status is non-zero, while ret is zero. */ if (wqnp) kmem_free(wqnp, sizeof (ibcm_arp_prwqn_t)); } ret = ib_s->status; mutex_exit(&ib_s->lock); arp_ibaddr_error: mutex_destroy(&ib_s->lock); cv_destroy(&ib_s->cv); kmem_free(ib_s, sizeof (ibcm_arp_streams_t)); if (ret) return (IBT_FAILURE); else return (IBT_SUCCESS); } /* * Routine to get list of "local" IP-ADDR to GID/P_KEY mapping information. * Optionally, if "gid" and/or "p_key" info are specified, then retrieve the * IP-ADDR info for that attribute only. */ static ibcm_arp_ip_t * ibcm_arp_ibd_gid2mac(ib_gid_t *gid, ib_pkey_t pkey, ibcm_arp_ibd_insts_t *ibdp) { ibcm_arp_ip_t *ipp; int i; for (i = 0, ipp = ibdp->ibcm_arp_ip; i < ibdp->ibcm_arp_ibd_cnt; i++, ipp++) { if ((ipp->ip_port_gid.gid_prefix == gid->gid_prefix) && (ipp->ip_port_gid.gid_guid == gid->gid_guid)) { if (pkey) { if (ipp->ip_pkey == pkey) return (ipp); else continue; } return (ipp); } } return (NULL); } static ibt_status_t ibcm_arp_ibd_mac2gid(ibcm_arp_ibd_insts_t *ibdp, ibt_ip_addr_t *srcip, ib_gid_t *sgid) { ibcm_arp_ip_t *ipp; int i; boolean_t found = B_FALSE; for (i = 0, ipp = ibdp->ibcm_arp_ip; i < ibdp->ibcm_arp_ibd_cnt; i++, ipp++) { IBTF_DPRINTF_L4(cmlog, "ibcm_arp_ibd_mac2gid: GID %llX:%llX", ipp->ip_port_gid.gid_prefix, ipp->ip_port_gid.gid_guid); if (srcip->family == ipp->ip_inet_family) { if ((srcip->family == AF_INET) && (bcmp(&srcip->un.ip4addr, &ipp->ip_cm_sin.sin_addr, sizeof (in_addr_t)) == 0)) { found = B_TRUE; } else if ((srcip->family == AF_INET6) && IN6_ARE_ADDR_EQUAL(&srcip->un.ip6addr, &ipp->ip_cm_sin6.sin6_addr)) { found = B_TRUE; } if (found) { *sgid = ipp->ip_port_gid; IBTF_DPRINTF_L4(cmlog, "ibcm_arp_ibd_mac2gid: " "Found GID %llX:%llX", sgid->gid_prefix, sgid->gid_guid); return (IBT_SUCCESS); } } else { IBTF_DPRINTF_L3(cmlog, "ibcm_arp_ibd_mac2gid: Different" " family keep searching..."); } } IBTF_DPRINTF_L3(cmlog, "ibcm_arp_ibd_mac2gid: Matching SRC info " "NOT Found"); return (IBT_SRC_IP_NOT_FOUND); } static int ibcm_arp_get_ibd_insts_cb(dev_info_t *dip, void *arg) { ibcm_arp_ibd_insts_t *ibds = (ibcm_arp_ibd_insts_t *)arg; ibcm_arp_ip_t *ipp; ib_pkey_t pkey; uint8_t port; ib_guid_t hca_guid; ib_gid_t port_gid; if (i_ddi_devi_attached(dip) && (strcmp(ddi_node_name(dip), "ibport") == 0) && (strstr(ddi_get_name_addr(dip), "ipib") != NULL)) { if (ibds->ibcm_arp_ibd_cnt >= ibds->ibcm_arp_ibd_alloc) { ibcm_arp_ip_t *tmp = NULL; uint8_t new_count; new_count = ibds->ibcm_arp_ibd_alloc + IBCM_ARP_IBD_INSTANCES; tmp = (ibcm_arp_ip_t *)kmem_zalloc( new_count * sizeof (ibcm_arp_ip_t), KM_SLEEP); bcopy(ibds->ibcm_arp_ip, tmp, ibds->ibcm_arp_ibd_alloc * sizeof (ibcm_arp_ip_t)); kmem_free(ibds->ibcm_arp_ip, ibds->ibcm_arp_ibd_alloc * sizeof (ibcm_arp_ip_t)); ibds->ibcm_arp_ibd_alloc = new_count; ibds->ibcm_arp_ip = tmp; } if (((hca_guid = ddi_prop_get_int64(DDI_DEV_T_ANY, dip, 0, "hca-guid", 0)) == 0) || ((port = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "port-number", 0)) == 0) || (ibt_get_port_state_byguid(hca_guid, port, &port_gid, NULL) != IBT_SUCCESS) || ((pkey = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "port-pkey", IB_PKEY_INVALID_LIMITED)) <= IB_PKEY_INVALID_FULL)) { return (DDI_WALK_CONTINUE); } ipp = &ibds->ibcm_arp_ip[ibds->ibcm_arp_ibd_cnt]; ipp->ip_inst = ddi_get_instance(dip); ipp->ip_pkey = pkey; ipp->ip_hca_guid = hca_guid; ipp->ip_port_gid = port_gid; ibds->ibcm_arp_ibd_cnt++; } return (DDI_WALK_CONTINUE); } static void ibcm_arp_get_ibd_insts(ibcm_arp_ibd_insts_t *ibds) { ddi_walk_devs(ddi_root_node(), ibcm_arp_get_ibd_insts_cb, ibds); } /* * Issue an ioctl down to IP. There are several similar versions of this * function (e.g., rpcib_do_ip_ioctl()); clearly a utility routine is needed. */ static int ibcm_do_ip_ioctl(int cmd, int len, void *arg) { vnode_t *kvp; TIUSER *tiptr; struct strioctl iocb; int err = 0; if (lookupname("/dev/udp", UIO_SYSSPACE, FOLLOW, NULLVPP, &kvp) != 0) return (EPROTO); if (t_kopen(NULL, kvp->v_rdev, FREAD|FWRITE, &tiptr, CRED()) != 0) { VN_RELE(kvp); return (EPROTO); } iocb.ic_cmd = cmd; iocb.ic_timout = 0; iocb.ic_len = len; iocb.ic_dp = (caddr_t)arg; err = kstr_ioctl(tiptr->fp->f_vnode, I_STR, (intptr_t)&iocb); (void) t_kclose(tiptr, 0); VN_RELE(kvp); return (err); } /* * Issue an SIOCGLIFCONF down to IP and return the result in `lifcp'. * lifcp->lifc_buf is dynamically allocated to be *bufsizep bytes. */ static int ibcm_do_lifconf(struct lifconf *lifcp, uint_t *bufsizep, sa_family_t family_loc) { int err; struct lifnum lifn; bzero(&lifn, sizeof (struct lifnum)); lifn.lifn_family = family_loc; err = ibcm_do_ip_ioctl(SIOCGLIFNUM, sizeof (struct lifnum), &lifn); if (err != 0) return (err); IBTF_DPRINTF_L4(cmlog, "ibcm_do_lifconf: Family %d, lifn_count %d", family_loc, lifn.lifn_count); /* * Pad the interface count to account for additional interfaces that * may have been configured between the SIOCGLIFNUM and SIOCGLIFCONF. */ lifn.lifn_count += 4; bzero(lifcp, sizeof (struct lifconf)); _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*lifcp)) lifcp->lifc_family = family_loc; lifcp->lifc_len = *bufsizep = lifn.lifn_count * sizeof (struct lifreq); lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_SLEEP); err = ibcm_do_ip_ioctl(SIOCGLIFCONF, sizeof (struct lifconf), lifcp); if (err != 0) { kmem_free(lifcp->lifc_buf, *bufsizep); return (err); } return (0); } /* * Fill in `ibds' with IP addresses tied to IFT_IB IP interfaces. Returns * B_TRUE if at least one address was filled in. */ static boolean_t ibcm_arp_get_ibd_ipaddr(ibcm_arp_ibd_insts_t *ibds, sa_family_t family_loc) { int i, nifs, naddr = 0; uint_t bufsize; struct lifconf lifc; struct lifreq *lifrp; ibcm_arp_ip_t *ipp; if (ibcm_do_lifconf(&lifc, &bufsize, family_loc) != 0) return (B_FALSE); nifs = lifc.lifc_len / sizeof (struct lifreq); IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibd_ipaddr: Family %d, nifs %d", family_loc, nifs); for (lifrp = lifc.lifc_req, i = 0; i < nifs && naddr < ibds->ibcm_arp_ibd_cnt; i++, lifrp++) { if (lifrp->lifr_type != IFT_IB) continue; ipp = &ibds->ibcm_arp_ip[naddr]; switch (lifrp->lifr_addr.ss_family) { case AF_INET: ipp->ip_inet_family = AF_INET; bcopy(&lifrp->lifr_addr, &ipp->ip_cm_sin, sizeof (struct sockaddr_in)); naddr++; break; case AF_INET6: ipp->ip_inet_family = AF_INET6; bcopy(&lifrp->lifr_addr, &ipp->ip_cm_sin6, sizeof (struct sockaddr_in6)); naddr++; break; } } kmem_free(lifc.lifc_buf, bufsize); return (naddr > 0); } ibt_status_t ibcm_arp_get_ibds(ibcm_arp_ibd_insts_t *ibdp, sa_family_t family_loc) { #ifdef DEBUG int i; #endif IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds(%p)", ibdp); ibcm_arp_get_ibd_insts(ibdp); IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_ibds: Found %d ibd instances", ibdp->ibcm_arp_ibd_cnt); if (ibdp->ibcm_arp_ibd_cnt == 0) return (IBT_SRC_IP_NOT_FOUND); /* Get the IP addresses of active ports. */ if (!ibcm_arp_get_ibd_ipaddr(ibdp, family_loc)) { IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_ibds: failed to get " "ibd instance: IBT_SRC_IP_NOT_FOUND"); return (IBT_SRC_IP_NOT_FOUND); } #ifdef DEBUG for (i = 0; i < ibdp->ibcm_arp_ibd_cnt; i++) { char my_buf[INET6_ADDRSTRLEN]; ibcm_arp_ip_t *aip = &ibdp->ibcm_arp_ip[i]; IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: ibd[%d]: Family %d " "Instance %d PKey 0x%lX \n HCAGUID 0x%llX SGID %llX:%llX", i, aip->ip_inet_family, aip->ip_inst, aip->ip_pkey, aip->ip_hca_guid, aip->ip_port_gid.gid_prefix, aip->ip_port_gid.gid_guid); if (aip->ip_inet_family == AF_INET) { IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: IPV4: %s", inet_ntop(AF_INET, &aip->ip_cm_sin.sin_addr, my_buf, sizeof (my_buf))); } else if (aip->ip_inet_family == AF_INET6) { IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: IPV6: %s", inet_ntop(AF_INET6, &aip->ip_cm_sin6.sin6_addr, my_buf, sizeof (my_buf))); } else { IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_ibds: Unknown " "Family %d", aip->ip_inet_family); } } #endif return (IBT_SUCCESS); } _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibtl_cm_port_list_t)) ibt_status_t ibcm_arp_get_srcip_plist(ibt_ip_path_attr_t *ipattr, ibt_path_flags_t flags, ibtl_cm_port_list_t **port_list_p) { ibt_path_attr_t attr; ibt_status_t ret; ibcm_arp_ibd_insts_t ibds; ibcm_arp_ip_t *ipp; ibtl_cm_port_list_t *plistp; ib_gid_t sgid; sa_family_t family_interested = AF_UNSPEC; IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_srcip_plist(%p, %llX)", ipattr, flags); if (ipattr->ipa_src_ip.family != AF_UNSPEC) family_interested = ipattr->ipa_src_ip.family; else family_interested = ipattr->ipa_dst_ip[0].family; sgid.gid_prefix = sgid.gid_guid = 0; bzero(&ibds, sizeof (ibcm_arp_ibd_insts_t)); ibds.ibcm_arp_ibd_alloc = IBCM_ARP_IBD_INSTANCES; ibds.ibcm_arp_ibd_cnt = 0; ibds.ibcm_arp_ip = (ibcm_arp_ip_t *)kmem_zalloc( ibds.ibcm_arp_ibd_alloc * sizeof (ibcm_arp_ip_t), KM_SLEEP); ret = ibcm_arp_get_ibds(&ibds, family_interested); if (ret != IBT_SUCCESS) { IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_srcip_plist: " "ibcm_arp_get_ibds failed : 0x%x", ret); goto srcip_plist_end; } if (ipattr->ipa_src_ip.family != AF_UNSPEC) { ret = ibcm_arp_ibd_mac2gid(&ibds, &ipattr->ipa_src_ip, &sgid); if (ret != IBT_SUCCESS) { IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_srcip_plist: " "SGID for the specified SRCIP Not found %X", ret); goto srcip_plist_end; } IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_srcip_plist: SGID " "%llX:%llX", sgid.gid_prefix, sgid.gid_guid); } bzero(&attr, sizeof (ibt_path_attr_t)); attr.pa_hca_guid = ipattr->ipa_hca_guid; attr.pa_hca_port_num = ipattr->ipa_hca_port_num; attr.pa_sgid = sgid; bcopy(&ipattr->ipa_mtu, &attr.pa_mtu, sizeof (ibt_mtu_req_t)); bcopy(&ipattr->ipa_srate, &attr.pa_srate, sizeof (ibt_srate_req_t)); bcopy(&ipattr->ipa_pkt_lt, &attr.pa_pkt_lt, sizeof (ibt_pkt_lt_req_t)); ret = ibtl_cm_get_active_plist(&attr, flags, port_list_p); if (ret == IBT_SUCCESS) { int i; uint8_t cnt; boolean_t no_srcip_configured = B_FALSE; uint8_t no_srcip_cnt = 0; plistp = port_list_p[0]; cnt = plistp->p_count; for (i = 0; i < cnt; i++, plistp++) { ipp = ibcm_arp_ibd_gid2mac(&plistp->p_sgid, 0, &ibds); if ((ipp == NULL) || (ipp->ip_inet_family == AF_UNSPEC)) { plistp->p_src_ip.family = AF_UNSPEC; no_srcip_configured = B_TRUE; no_srcip_cnt++; IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_srcip_plist: SrcIP NOT " "Configured for GID %llX:%llX", plistp->p_sgid.gid_prefix, plistp->p_sgid.gid_guid); } else { IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_srcip_plist: GID %llX:%llX", plistp->p_sgid.gid_prefix, plistp->p_sgid.gid_guid); if (ipp->ip_inet_family == AF_INET) { plistp->p_src_ip.family = AF_INET; bcopy(&ipp->ip_cm_sin.sin_addr, &plistp->p_src_ip.un.ip4addr, sizeof (in_addr_t)); } else if (ipp->ip_inet_family == AF_INET6) { plistp->p_src_ip.family = AF_INET6; bcopy(&ipp->ip_cm_sin6.sin6_addr, &plistp->p_src_ip.un.ip6addr, sizeof (in6_addr_t)); } IBCM_PRINT_IP("ibcm_arp_get_srcip_plist: " "IP Addr is:", &plistp->p_src_ip); } } if (no_srcip_configured) { ibtl_cm_port_list_t *n_plistp, *tmp_n_plistp; uint8_t new_cnt; new_cnt = cnt - no_srcip_cnt; /* * Looks like some of the SRC GID we found have no * IP ADDR configured, so remove these entries from * our list. */ plistp = port_list_p[0]; IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_srcip_plist: " "Only %d SGID (%d/%d) have SrcIP Configured", new_cnt, no_srcip_cnt, cnt); if (new_cnt) { /* Allocate Memory to hold Src Point info. */ n_plistp = kmem_zalloc(new_cnt * sizeof (ibtl_cm_port_list_t), KM_SLEEP); tmp_n_plistp = n_plistp; for (i = 0; i < cnt; i++, plistp++) { if (plistp->p_src_ip.family == AF_UNSPEC) continue; bcopy(plistp, n_plistp, sizeof (ibtl_cm_port_list_t)); n_plistp->p_count = new_cnt; n_plistp++; } plistp = port_list_p[0]; *port_list_p = tmp_n_plistp; } else { /* * All entries we have, do not have IP-Addr * configured so return empty hand. */ IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_srcip_plist: None of SGID " "found have SrcIP Configured"); *port_list_p = NULL; ret = IBT_SRC_IP_NOT_FOUND; } IBTF_DPRINTF_L4(cmlog, "FREE OLD list %p, NEW list is " "%p - %p", plistp, port_list_p, *port_list_p); kmem_free(plistp, cnt * sizeof (ibtl_cm_port_list_t)); } } srcip_plist_end: if (ibds.ibcm_arp_ip) kmem_free(ibds.ibcm_arp_ip, ibds.ibcm_arp_ibd_alloc * sizeof (ibcm_arp_ip_t)); return (ret); }