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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #define AF_INET_OFFLOAD 30 29 30 #include <sys/sockio.h> 31 #include <sys/stream.h> 32 #include <sys/errno.h> 33 #include <sys/cmn_err.h> 34 #include <sys/strsun.h> 35 #include <inet/common.h> 36 #include <net/if.h> 37 #include <inet/mi.h> 38 #include <sys/t_kuser.h> 39 #include <sys/stropts.h> 40 #include <sys/pathname.h> 41 #include <sys/kstr.h> 42 #include <sys/timod.h> 43 #include <sys/ib/clients/rds/rds.h> 44 #include <sys/ib/clients/rds/rds_transport.h> 45 46 static sin_t sin_null; /* Zero address for quick clears */ 47 48 #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 49 50 #define isalpha(ch) (((ch) >= 'a' && (ch) <= 'z') || \ 51 ((ch) >= 'A' && (ch) <= 'Z')) 52 53 54 /* 55 * Just pass the ioctl to IP and the result to the caller. 56 */ 57 int 58 rds_do_ip_ioctl(int cmd, int len, caddr_t arg) 59 { 60 vnode_t *kvp, *vp; 61 TIUSER *tiptr; 62 struct strioctl iocb; 63 int err = 0; 64 65 if (lookupname("/dev/udp", UIO_SYSSPACE, FOLLOW, NULLVPP, 66 &kvp) == 0) { 67 if (t_kopen((file_t *)NULL, kvp->v_rdev, FREAD|FWRITE, 68 &tiptr, CRED()) == 0) { 69 vp = tiptr->fp->f_vnode; 70 } else { 71 VN_RELE(kvp); 72 return (EPROTO); 73 } 74 } else { 75 return (EPROTO); 76 } 77 78 iocb.ic_cmd = cmd; 79 iocb.ic_timout = 0; 80 iocb.ic_len = len; 81 iocb.ic_dp = arg; 82 err = kstr_ioctl(vp, I_STR, (intptr_t)&iocb); 83 (void) t_kclose(tiptr, 0); 84 VN_RELE(kvp); 85 return (err); 86 } 87 88 static int 89 rds_dl_info(ldi_handle_t lh, dl_info_ack_t *info) 90 { 91 dl_info_req_t *info_req; 92 union DL_primitives *dl_prim; 93 mblk_t *mp; 94 int error; 95 96 if ((mp = allocb(sizeof (dl_info_req_t), BPRI_MED)) == NULL) { 97 return (ENOMEM); 98 } 99 100 mp->b_datap->db_type = M_PROTO; 101 102 info_req = (dl_info_req_t *)(uintptr_t)mp->b_wptr; 103 mp->b_wptr += sizeof (dl_info_req_t); 104 info_req->dl_primitive = DL_INFO_REQ; 105 106 if ((error = ldi_putmsg(lh, mp)) != 0) { 107 return (error); 108 } 109 if ((error = ldi_getmsg(lh, &mp, (timestruc_t *)NULL)) != 0) { 110 return (error); 111 } 112 113 dl_prim = (union DL_primitives *)(uintptr_t)mp->b_rptr; 114 switch (dl_prim->dl_primitive) { 115 case DL_INFO_ACK: 116 if (((uintptr_t)mp->b_wptr - (uintptr_t)mp->b_rptr) < 117 sizeof (dl_info_ack_t)) { 118 error = -1; 119 } else { 120 *info = *(dl_info_ack_t *)(uintptr_t)mp->b_rptr; 121 error = 0; 122 } 123 break; 124 default: 125 error = -1; 126 break; 127 } 128 129 freemsg(mp); 130 return (error); 131 } 132 133 134 static boolean_t 135 rds_is_ib_interface(char *name) 136 { 137 138 char dev_path[MAXPATHLEN]; 139 ldi_handle_t lh; 140 dl_info_ack_t info; 141 int ret = 0; 142 int i = 0; 143 144 (void) strcpy(dev_path, "/dev/"); 145 146 /* 147 * ibd devices are only style 2 devices 148 * so we will open only style 2 devices 149 * by ignoring the ppa 150 */ 151 while (isalpha(name[i])) { 152 i++; 153 } 154 155 if (i == 0) { 156 /* 157 * null name. 158 */ 159 return (B_FALSE); 160 } 161 162 if (strncmp("lo", name, i) == 0) { 163 /* 164 * loopback interface is considered RDS capable 165 */ 166 return (B_TRUE); 167 } 168 169 (void) strncat((dev_path + sizeof ("/dev/") -1), name, i); 170 171 ret = ldi_open_by_name(dev_path, FREAD|FWRITE, kcred, &lh, rds_li); 172 if (ret != 0) { 173 return (B_FALSE); 174 } 175 176 ret = rds_dl_info(lh, &info); 177 178 (void) ldi_close(lh, FREAD|FWRITE, kcred); 179 180 if (ret != 0 || (info.dl_mac_type != DL_IB && 181 !rds_transport_ops->rds_transport_if_lookup_by_name(name))) { 182 return (B_FALSE); 183 } 184 185 return (B_TRUE); 186 } 187 188 void 189 rds_ioctl_copyin_done(queue_t *q, mblk_t *mp) 190 { 191 char *addr; 192 mblk_t *mp1; 193 int err = 0; 194 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 195 196 if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) { 197 err = EPROTO; 198 goto done; 199 } 200 201 addr = (char *)mp1->b_rptr; 202 203 switch (iocp->ioc_cmd) { 204 205 case SIOCGIFNUM: { 206 /* Get number of interfaces. */ 207 struct ifconf kifc; 208 struct ifreq *ifr; 209 int num_ifs; 210 int n; 211 212 err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (int), 213 (char *)&num_ifs); 214 if (err != 0) { 215 break; 216 } 217 218 kifc.ifc_len = num_ifs * sizeof (struct ifreq); 219 kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP); 220 err = rds_do_ip_ioctl(SIOCGIFCONF, 221 sizeof (struct ifconf), (caddr_t)&kifc); 222 if (err != 0) { 223 kmem_free(kifc.ifc_buf, kifc.ifc_len); 224 break; 225 } 226 ifr = kifc.ifc_req; 227 n = num_ifs; 228 for (num_ifs = 0; n > 0; ifr++) { 229 if (rds_is_ib_interface(ifr->ifr_name)) { 230 num_ifs++; 231 } 232 n--; 233 } 234 *((int *)(uintptr_t)addr) = num_ifs; 235 kmem_free(kifc.ifc_buf, kifc.ifc_len); 236 } 237 break; 238 239 case O_SIOCGIFCONF: 240 case SIOCGIFCONF: { 241 STRUCT_HANDLE(ifconf, ifc); 242 caddr_t ubuf_addr; 243 int ubuf_size; 244 struct ifconf kifc; 245 struct ifreq *ifr, *ptr; 246 int num_ifs; 247 248 STRUCT_SET_HANDLE(ifc, iocp->ioc_flag, 249 (struct ifconf *)(uintptr_t)addr); 250 251 ubuf_size = STRUCT_FGET(ifc, ifc_len); 252 ubuf_addr = STRUCT_FGETP(ifc, ifc_buf); 253 254 err = rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), 255 (char *)&num_ifs); 256 if (err != 0) { 257 break; 258 } 259 260 kifc.ifc_len = num_ifs * sizeof (struct ifreq); 261 kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP); 262 err = rds_do_ip_ioctl(iocp->ioc_cmd, 263 sizeof (struct ifconf), (caddr_t)&kifc); 264 if (err != 0) { 265 kmem_free(kifc.ifc_buf, kifc.ifc_len); 266 break; 267 } 268 mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE); 269 if (mp1 == NULL) { 270 err = ENOMEM; 271 kmem_free(kifc.ifc_buf, ubuf_size); 272 break; 273 } 274 275 ifr = kifc.ifc_req; 276 ptr = (struct ifreq *)(uintptr_t)mp1->b_rptr; 277 for (; num_ifs > 0 && 278 (int)((uintptr_t)mp1->b_wptr - (uintptr_t)mp1->b_rptr) < 279 ubuf_size; num_ifs--, ifr++) { 280 if (rds_is_ib_interface(ifr->ifr_name)) { 281 ifr->ifr_addr.sa_family = AF_INET_OFFLOAD; 282 bcopy((caddr_t)ifr, ptr, sizeof (struct ifreq)); 283 ptr++; 284 mp1->b_wptr = (uchar_t *)ptr; 285 } 286 } 287 288 STRUCT_FSET(ifc, ifc_len, (int)((uintptr_t)mp1->b_wptr - 289 (uintptr_t)mp1->b_rptr)); 290 291 kmem_free(kifc.ifc_buf, kifc.ifc_len); 292 } 293 break; 294 case SIOCGIFMTU: 295 err = rds_do_ip_ioctl(iocp->ioc_cmd, 296 sizeof (struct ifreq), addr); 297 break; 298 299 case SIOCGIFFLAGS: 300 err = rds_do_ip_ioctl(iocp->ioc_cmd, 301 sizeof (struct ifreq), addr); 302 break; 303 case TI_GETMYNAME: { 304 305 rds_t *rds; 306 STRUCT_HANDLE(strbuf, sb); 307 ipaddr_t v4addr; 308 uint16_t port; 309 int addrlen; 310 sin_t *sin; 311 312 STRUCT_SET_HANDLE(sb, 313 ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag, 314 (void *)(uintptr_t)addr); 315 rds = (rds_t *)q->q_ptr; 316 ASSERT(rds->rds_family == AF_INET_OFFLOAD); 317 addrlen = sizeof (sin_t); 318 v4addr = rds->rds_src; 319 port = rds->rds_port; 320 mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, 321 B_TRUE); 322 if (mp1 == NULL) 323 return; 324 STRUCT_FSET(sb, len, (int)sizeof (sin_t)); 325 sin = (sin_t *)(uintptr_t)mp1->b_rptr; 326 mp1->b_wptr = (uchar_t *)&sin[1]; 327 *sin = sin_null; 328 sin->sin_family = AF_INET_OFFLOAD; 329 sin->sin_addr.s_addr = v4addr; 330 sin->sin_port = port; 331 332 } 333 break; 334 default: 335 err = EOPNOTSUPP; 336 break; 337 } 338 if (err == 0) { 339 mi_copyout(q, mp); 340 return; 341 } 342 done: 343 mi_copy_done(q, mp, err); 344 } 345 346 347 void 348 rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp) 349 { 350 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 351 int copyin_size; 352 353 if (mp->b_cont == NULL) { 354 iocp->ioc_error = EINVAL; 355 mp->b_datap->db_type = M_IOCNAK; 356 iocp->ioc_count = 0; 357 qreply(q, mp); 358 return; 359 } 360 361 switch (iocp->ioc_cmd) { 362 case O_SIOCGIFCONF: 363 case SIOCGIFCONF: 364 if (iocp->ioc_count == TRANSPARENT) 365 copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag); 366 else 367 copyin_size = iocp->ioc_count; 368 break; 369 370 case SIOCGIFNUM: 371 copyin_size = sizeof (int); 372 break; 373 case SIOCGIFFLAGS: 374 case SIOCGIFMTU: 375 copyin_size = sizeof (struct ifreq); 376 break; 377 case TI_GETMYNAME: 378 copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag); 379 break; 380 } 381 mi_copyin(q, mp, NULL, copyin_size); 382 } 383 384 void 385 rds_ioctl(queue_t *q, mblk_t *mp) 386 { 387 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 388 389 390 switch (iocp->ioc_cmd) { 391 case O_SIOCGIFCONF: 392 case SIOCGIFCONF: 393 case SIOCGIFNUM: 394 case SIOCGIFMTU: 395 case SIOCGIFFLAGS: 396 case TI_GETMYNAME: 397 rds_ioctl_copyin_setup(q, mp); 398 break; 399 default: 400 cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n"); 401 miocnak(q, mp, 0, ENOTSUP); 402 break; 403 } 404 } 405 406 boolean_t 407 rds_verify_bind_address(ipaddr_t addr) 408 { 409 int numifs; 410 struct ifconf kifc; 411 struct ifreq *ifr; 412 boolean_t ret = B_FALSE; 413 414 415 if (rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), (caddr_t)&numifs)) { 416 return (ret); 417 } 418 419 kifc.ifc_len = numifs * sizeof (struct ifreq); 420 kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP); 421 422 if (rds_do_ip_ioctl(SIOCGIFCONF, sizeof (struct ifconf), 423 (caddr_t)&kifc)) { 424 goto done; 425 } 426 427 ifr = kifc.ifc_req; 428 for (numifs = kifc.ifc_len / sizeof (struct ifreq); 429 numifs > 0; numifs--, ifr++) { 430 struct sockaddr_in *sin; 431 432 sin = (struct sockaddr_in *)(uintptr_t)&ifr->ifr_addr; 433 if ((sin->sin_addr.s_addr == addr) && 434 rds_is_ib_interface(ifr->ifr_name)) { 435 ret = B_TRUE; 436 break; 437 } 438 } 439 440 done: 441 kmem_free(kifc.ifc_buf, kifc.ifc_len); 442 return (ret); 443 } 444