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