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 /* 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/ddi.h> 28 #include <sys/sunddi.h> 29 #include <sys/strsubr.h> 30 #include <sys/socket.h> 31 #include <net/if_arp.h> 32 #include <net/if_types.h> 33 #include <sys/sockio.h> 34 #include <sys/pathname.h> 35 36 #include <sys/ib/mgt/ibcm/ibcm_arp.h> 37 38 #include <sys/kstr.h> 39 #include <sys/t_kuser.h> 40 41 extern char cmlog[]; 42 43 extern int ibcm_resolver_pr_lookup(ibcm_arp_streams_t *ib_s, 44 ibt_ip_addr_t *dst_addr, ibt_ip_addr_t *src_addr, zoneid_t myzoneid); 45 extern void ibcm_arp_delete_prwqn(ibcm_arp_prwqn_t *wqnp); 46 47 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibt_ip_addr_s)) 48 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_ip_t)) 49 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_ibd_insts_t)) 50 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_prwqn_t)) 51 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", sockaddr_in)) 52 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", sockaddr_in6)) 53 54 int ibcm_printip = 0; 55 56 /* 57 * Function: 58 * ibcm_ip_print 59 * Input: 60 * label Arbitrary qualifying string 61 * ipa Pointer to IP Address to print 62 */ 63 void 64 ibcm_ip_print(char *label, ibt_ip_addr_t *ipaddr) 65 { 66 char buf[INET6_ADDRSTRLEN]; 67 68 if (ipaddr->family == AF_INET) { 69 IBTF_DPRINTF_L2(cmlog, "%s: %s", label, 70 inet_ntop(AF_INET, &ipaddr->un.ip4addr, buf, sizeof (buf))); 71 } else if (ipaddr->family == AF_INET6) { 72 IBTF_DPRINTF_L2(cmlog, "%s: %s", label, inet_ntop(AF_INET6, 73 &ipaddr->un.ip6addr, buf, sizeof (buf))); 74 } else { 75 IBTF_DPRINTF_L2(cmlog, "%s: IP ADDR NOT SPECIFIED ", label); 76 } 77 } 78 79 80 ibt_status_t 81 ibcm_arp_get_ibaddr(zoneid_t myzoneid, ibt_ip_addr_t srcaddr, 82 ibt_ip_addr_t destaddr, ib_gid_t *sgid, ib_gid_t *dgid, 83 ibt_ip_addr_t *saddrp) 84 { 85 ibcm_arp_streams_t *ib_s; 86 ibcm_arp_prwqn_t *wqnp; 87 int ret = 0; 88 int len; 89 90 IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibaddr(%d, %p, %p, %p, %p, %p)", 91 myzoneid, srcaddr, destaddr, sgid, dgid, saddrp); 92 93 ib_s = (ibcm_arp_streams_t *)kmem_zalloc(sizeof (ibcm_arp_streams_t), 94 KM_SLEEP); 95 96 mutex_init(&ib_s->lock, NULL, MUTEX_DEFAULT, NULL); 97 cv_init(&ib_s->cv, NULL, CV_DRIVER, NULL); 98 99 mutex_enter(&ib_s->lock); 100 ib_s->done = B_FALSE; 101 mutex_exit(&ib_s->lock); 102 103 ret = ibcm_resolver_pr_lookup(ib_s, &destaddr, &srcaddr, myzoneid); 104 105 IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_ibaddr: ibcm_resolver_pr_lookup " 106 "returned: %d", ret); 107 if (ret == 0) { 108 mutex_enter(&ib_s->lock); 109 while (ib_s->done != B_TRUE) 110 cv_wait(&ib_s->cv, &ib_s->lock); 111 mutex_exit(&ib_s->lock); 112 } 113 114 mutex_enter(&ib_s->lock); 115 wqnp = ib_s->wqnp; 116 if (ib_s->status == 0) { 117 if (sgid) 118 *sgid = wqnp->sgid; 119 if (dgid) 120 *dgid = wqnp->dgid; 121 /* 122 * If the user supplied a address, then verify we got 123 * for the same address. 124 */ 125 if (wqnp->usrc_addr.family && sgid) { 126 len = (wqnp->usrc_addr.family == AF_INET) ? 127 IP_ADDR_LEN : sizeof (in6_addr_t); 128 if (bcmp(&wqnp->usrc_addr.un, 129 &wqnp->src_addr.un, len)) { 130 IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_ibaddr: " 131 "srcaddr mismatch"); 132 133 /* Clean-up old data, and reset the done flag */ 134 ibcm_arp_delete_prwqn(wqnp); 135 ib_s->done = B_FALSE; 136 mutex_exit(&ib_s->lock); 137 138 ret = ibcm_resolver_pr_lookup(ib_s, &srcaddr, 139 &srcaddr, myzoneid); 140 if (ret == 0) { 141 mutex_enter(&ib_s->lock); 142 while (ib_s->done != B_TRUE) 143 cv_wait(&ib_s->cv, &ib_s->lock); 144 mutex_exit(&ib_s->lock); 145 } 146 mutex_enter(&ib_s->lock); 147 wqnp = ib_s->wqnp; 148 if (ib_s->status == 0) { 149 if (sgid) 150 *sgid = wqnp->dgid; 151 152 if (saddrp) 153 bcopy(&wqnp->src_addr, saddrp, 154 sizeof (ibt_ip_addr_t)); 155 156 IBTF_DPRINTF_L4(cmlog, 157 "ibcm_arp_get_ibaddr: " 158 "SGID: %llX:%llX DGID: %llX:%llX", 159 sgid->gid_prefix, sgid->gid_guid, 160 dgid->gid_prefix, dgid->gid_guid); 161 162 ibcm_arp_delete_prwqn(wqnp); 163 } else if (ret == 0) { 164 if (wqnp) 165 kmem_free(wqnp, 166 sizeof (ibcm_arp_prwqn_t)); 167 } 168 goto arp_ibaddr_done; 169 } 170 } 171 172 if (saddrp) 173 bcopy(&wqnp->src_addr, saddrp, sizeof (ibt_ip_addr_t)); 174 175 IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibaddr: SGID: %llX:%llX" 176 " DGID: %llX:%llX", sgid->gid_prefix, sgid->gid_guid, 177 dgid->gid_prefix, dgid->gid_guid); 178 179 ibcm_arp_delete_prwqn(wqnp); 180 } else if (ret == 0) { 181 /* 182 * We come here only when lookup has returned empty (failed) 183 * via callback routine. 184 * i.e. ib_s->status is non-zero, while ret is zero. 185 */ 186 if (wqnp) 187 kmem_free(wqnp, sizeof (ibcm_arp_prwqn_t)); 188 } 189 arp_ibaddr_done: 190 ret = ib_s->status; 191 mutex_exit(&ib_s->lock); 192 193 arp_ibaddr_error: 194 195 mutex_destroy(&ib_s->lock); 196 cv_destroy(&ib_s->cv); 197 kmem_free(ib_s, sizeof (ibcm_arp_streams_t)); 198 199 if (ret) 200 return (IBT_FAILURE); 201 else 202 return (IBT_SUCCESS); 203 } 204 205 206 static int 207 ibcm_arp_get_ibd_insts_cb(dev_info_t *dip, void *arg) 208 { 209 ibcm_arp_ibd_insts_t *ibds = (ibcm_arp_ibd_insts_t *)arg; 210 ibcm_arp_ip_t *ipp; 211 ib_pkey_t pkey; 212 uint8_t port; 213 ib_guid_t hca_guid; 214 ib_gid_t port_gid; 215 216 if (i_ddi_devi_attached(dip) && 217 (strcmp(ddi_node_name(dip), "ibport") == 0) && 218 (strstr(ddi_get_name_addr(dip), "ipib") != NULL)) { 219 220 if (ibds->ibcm_arp_ibd_cnt >= ibds->ibcm_arp_ibd_alloc) { 221 ibcm_arp_ip_t *tmp = NULL; 222 uint8_t new_count; 223 224 new_count = ibds->ibcm_arp_ibd_alloc + 225 IBCM_ARP_IBD_INSTANCES; 226 227 tmp = (ibcm_arp_ip_t *)kmem_zalloc( 228 new_count * sizeof (ibcm_arp_ip_t), KM_SLEEP); 229 bcopy(ibds->ibcm_arp_ip, tmp, 230 ibds->ibcm_arp_ibd_alloc * sizeof (ibcm_arp_ip_t)); 231 kmem_free(ibds->ibcm_arp_ip, 232 ibds->ibcm_arp_ibd_alloc * sizeof (ibcm_arp_ip_t)); 233 ibds->ibcm_arp_ibd_alloc = new_count; 234 ibds->ibcm_arp_ip = tmp; 235 } 236 237 if (((hca_guid = ddi_prop_get_int64(DDI_DEV_T_ANY, dip, 0, 238 "hca-guid", 0)) == 0) || 239 ((port = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, 240 "port-number", 0)) == 0) || 241 (ibt_get_port_state_byguid(hca_guid, port, &port_gid, 242 NULL) != IBT_SUCCESS) || 243 ((pkey = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, 244 "port-pkey", IB_PKEY_INVALID_LIMITED)) <= 245 IB_PKEY_INVALID_FULL)) { 246 return (DDI_WALK_CONTINUE); 247 } 248 249 ipp = &ibds->ibcm_arp_ip[ibds->ibcm_arp_ibd_cnt]; 250 ipp->ip_inst = ddi_get_instance(dip); 251 ipp->ip_pkey = pkey; 252 ipp->ip_hca_guid = hca_guid; 253 ipp->ip_port_gid = port_gid; 254 ibds->ibcm_arp_ibd_cnt++; 255 } 256 return (DDI_WALK_CONTINUE); 257 } 258 259 static void 260 ibcm_arp_get_ibd_insts(ibcm_arp_ibd_insts_t *ibds) 261 { 262 ddi_walk_devs(ddi_root_node(), ibcm_arp_get_ibd_insts_cb, ibds); 263 } 264 265 /* 266 * Issue an ioctl down to IP. There are several similar versions of this 267 * function (e.g., rpcib_do_ip_ioctl()); clearly a utility routine is needed. 268 */ 269 static int 270 ibcm_do_ip_ioctl(int cmd, int len, void *arg) 271 { 272 vnode_t *kkvp; 273 TIUSER *tiptr; 274 struct strioctl iocb; 275 int err = 0; 276 277 if (lookupname("/dev/udp", UIO_SYSSPACE, FOLLOW, NULLVPP, &kkvp) != 0) 278 return (EPROTO); 279 280 if (t_kopen(NULL, kkvp->v_rdev, FREAD|FWRITE, &tiptr, CRED()) != 0) { 281 VN_RELE(kkvp); 282 return (EPROTO); 283 } 284 285 iocb.ic_cmd = cmd; 286 iocb.ic_timout = 0; 287 iocb.ic_len = len; 288 iocb.ic_dp = (caddr_t)arg; 289 err = kstr_ioctl(tiptr->fp->f_vnode, I_STR, (intptr_t)&iocb); 290 (void) t_kclose(tiptr, 0); 291 VN_RELE(kkvp); 292 return (err); 293 } 294 295 /* 296 * Issue an SIOCGLIFCONF down to IP and return the result in `lifcp'. 297 * lifcp->lifc_buf is dynamically allocated to be *bufsizep bytes. 298 */ 299 static int 300 ibcm_do_lifconf(struct lifconf *lifcp, uint_t *bufsizep, sa_family_t family_loc) 301 { 302 int err; 303 struct lifnum lifn; 304 305 bzero(&lifn, sizeof (struct lifnum)); 306 lifn.lifn_family = family_loc; 307 308 err = ibcm_do_ip_ioctl(SIOCGLIFNUM, sizeof (struct lifnum), &lifn); 309 if (err != 0) 310 return (err); 311 312 IBTF_DPRINTF_L4(cmlog, "ibcm_do_lifconf: Family %d, lifn_count %d", 313 family_loc, lifn.lifn_count); 314 /* 315 * Pad the interface count to account for additional interfaces that 316 * may have been configured between the SIOCGLIFNUM and SIOCGLIFCONF. 317 */ 318 lifn.lifn_count += 4; 319 320 bzero(lifcp, sizeof (struct lifconf)); 321 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*lifcp)) 322 lifcp->lifc_family = family_loc; 323 lifcp->lifc_len = *bufsizep = lifn.lifn_count * sizeof (struct lifreq); 324 lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_SLEEP); 325 326 err = ibcm_do_ip_ioctl(SIOCGLIFCONF, sizeof (struct lifconf), lifcp); 327 if (err != 0) { 328 kmem_free(lifcp->lifc_buf, *bufsizep); 329 return (err); 330 } 331 return (0); 332 } 333 334 /* 335 * Fill in `ibds' with IP addresses tied to IFT_IB IP interfaces. Returns 336 * B_TRUE if at least one address was filled in. 337 */ 338 static boolean_t 339 ibcm_arp_get_ibd_ipaddr(ibcm_arp_ibd_insts_t *ibds, sa_family_t family_loc) 340 { 341 int i, nifs, naddr = 0; 342 uint_t bufsize; 343 struct lifconf lifc; 344 struct lifreq *lifrp; 345 ibcm_arp_ip_t *ipp; 346 347 if (ibcm_do_lifconf(&lifc, &bufsize, family_loc) != 0) 348 return (B_FALSE); 349 350 nifs = lifc.lifc_len / sizeof (struct lifreq); 351 352 IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibd_ipaddr: Family %d, nifs %d", 353 family_loc, nifs); 354 355 for (lifrp = lifc.lifc_req, i = 0; 356 i < nifs && naddr < ibds->ibcm_arp_ibd_cnt; i++, lifrp++) { 357 if (lifrp->lifr_type != IFT_IB) 358 continue; 359 360 ipp = &ibds->ibcm_arp_ip[naddr]; 361 switch (lifrp->lifr_addr.ss_family) { 362 case AF_INET: 363 ipp->ip_inet_family = AF_INET; 364 bcopy(&lifrp->lifr_addr, &ipp->ip_cm_sin, 365 sizeof (struct sockaddr_in)); 366 naddr++; 367 break; 368 case AF_INET6: 369 ipp->ip_inet_family = AF_INET6; 370 bcopy(&lifrp->lifr_addr, &ipp->ip_cm_sin6, 371 sizeof (struct sockaddr_in6)); 372 naddr++; 373 break; 374 } 375 } 376 377 kmem_free(lifc.lifc_buf, bufsize); 378 return (naddr > 0); 379 } 380 381 ibt_status_t 382 ibcm_arp_get_ibds(ibcm_arp_ibd_insts_t *ibdp, sa_family_t family_loc) 383 { 384 #ifdef DEBUG 385 int i; 386 #endif 387 388 IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds(%p)", ibdp); 389 390 ibcm_arp_get_ibd_insts(ibdp); 391 392 IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_ibds: Found %d ibd instances", 393 ibdp->ibcm_arp_ibd_cnt); 394 395 if (ibdp->ibcm_arp_ibd_cnt == 0) 396 return (IBT_SRC_IP_NOT_FOUND); 397 398 /* Get the IP addresses of active ports. */ 399 if (!ibcm_arp_get_ibd_ipaddr(ibdp, family_loc)) { 400 IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_ibds: failed to get " 401 "ibd instance: IBT_SRC_IP_NOT_FOUND"); 402 return (IBT_SRC_IP_NOT_FOUND); 403 } 404 405 #ifdef DEBUG 406 for (i = 0; i < ibdp->ibcm_arp_ibd_cnt; i++) { 407 char my_buf[INET6_ADDRSTRLEN]; 408 ibcm_arp_ip_t *aip = &ibdp->ibcm_arp_ip[i]; 409 410 IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: ibd[%d]: Family %d " 411 "Instance %d PKey 0x%lX \n HCAGUID 0x%llX SGID %llX:%llX", 412 i, aip->ip_inet_family, aip->ip_inst, aip->ip_pkey, 413 aip->ip_hca_guid, aip->ip_port_gid.gid_prefix, 414 aip->ip_port_gid.gid_guid); 415 if (aip->ip_inet_family == AF_INET) { 416 IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: IPV4: %s", 417 inet_ntop(AF_INET, &aip->ip_cm_sin.sin_addr, my_buf, 418 sizeof (my_buf))); 419 } else if (aip->ip_inet_family == AF_INET6) { 420 IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: IPV6: %s", 421 inet_ntop(AF_INET6, &aip->ip_cm_sin6.sin6_addr, 422 my_buf, sizeof (my_buf))); 423 } else { 424 IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_ibds: Unknown " 425 "Family %d", aip->ip_inet_family); 426 } 427 } 428 #endif 429 430 return (IBT_SUCCESS); 431 } 432