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 2008 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 * Just pass the ioctl to IP and the result to the caller. 55 */ 56 int 57 rds_do_ip_ioctl(int cmd, int len, caddr_t arg) 58 { 59 vnode_t *kvp, *vp; 60 TIUSER *tiptr; 61 struct strioctl iocb; 62 k_sigset_t smask; 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 sigintr(&smask, 0); 83 err = kstr_ioctl(vp, I_STR, (intptr_t)&iocb); 84 sigunintr(&smask); 85 (void) t_kclose(tiptr, 0); 86 VN_RELE(kvp); 87 return (err); 88 } 89 90 /* 91 * Return 0 if the interface is IB. 92 * Return error (>0) if any error is encountered during processing. 93 * Return -1 if the interface is not IB and no error. 94 */ 95 static int 96 rds_is_ib_interface(char *name) 97 { 98 99 char dev_path[MAXPATHLEN]; 100 char devname[MAXNAMELEN]; 101 ldi_handle_t lh; 102 dl_info_ack_t info; 103 int ret = 0; 104 int i; 105 k_sigset_t smask; 106 107 /* 108 * ibd devices are only style 2 devices 109 * so we will open only style 2 devices 110 * by ignoring the ppa 111 */ 112 i = strlen(name) - 1; 113 while ((i >= 0) && (!isalpha(name[i]))) i--; 114 if (i < 0) { 115 /* Invalid interface name, no alphabet */ 116 return (-1); 117 } 118 (void) strncpy(devname, name, i + 1); 119 devname[i + 1] = '\0'; 120 121 if (strcmp("lo", devname) == 0) { 122 /* 123 * loopback interface is considered RDS capable 124 */ 125 return (0); 126 } 127 128 (void) strncpy(dev_path, "/dev/", MAXPATHLEN); 129 if (strlcat(dev_path, devname, MAXPATHLEN) >= MAXPATHLEN) { 130 /* string overflow */ 131 return (-1); 132 } 133 134 ret = ldi_open_by_name(dev_path, FREAD|FWRITE, kcred, &lh, rds_li); 135 if (ret != 0) { 136 return (ret); 137 } 138 139 sigintr(&smask, 0); 140 ret = dl_info(lh, &info, NULL, NULL, NULL); 141 sigunintr(&smask); 142 (void) ldi_close(lh, FREAD|FWRITE, kcred); 143 if (ret != 0) { 144 return (ret); 145 } 146 147 if (info.dl_mac_type != DL_IB && 148 !rds_transport_ops->rds_transport_if_lookup_by_name(devname)) { 149 return (-1); 150 } 151 152 return (0); 153 } 154 155 void 156 rds_ioctl_copyin_done(queue_t *q, mblk_t *mp) 157 { 158 char *addr; 159 mblk_t *mp1; 160 int err = 0; 161 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 162 163 if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) { 164 err = EPROTO; 165 goto done; 166 } 167 168 addr = (char *)mp1->b_rptr; 169 170 switch (iocp->ioc_cmd) { 171 172 case SIOCGIFNUM: { 173 /* Get number of interfaces. */ 174 struct ifconf kifc; 175 struct ifreq *ifr; 176 int num_ifs; 177 int n; 178 179 err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (int), 180 (char *)&num_ifs); 181 if (err != 0) { 182 break; 183 } 184 185 kifc.ifc_len = num_ifs * sizeof (struct ifreq); 186 kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP); 187 err = rds_do_ip_ioctl(SIOCGIFCONF, 188 sizeof (struct ifconf), (caddr_t)&kifc); 189 if (err != 0) { 190 kmem_free(kifc.ifc_buf, kifc.ifc_len); 191 break; 192 } 193 ifr = kifc.ifc_req; 194 n = num_ifs; 195 for (num_ifs = 0; n > 0; ifr++) { 196 err = rds_is_ib_interface(ifr->ifr_name); 197 if (err == 0) { 198 num_ifs++; 199 } else if (err > 0) { 200 num_ifs = 0; 201 break; 202 } else { 203 err = 0; 204 } 205 n--; 206 } 207 *((int *)(uintptr_t)addr) = num_ifs; 208 kmem_free(kifc.ifc_buf, kifc.ifc_len); 209 } 210 break; 211 212 case O_SIOCGIFCONF: 213 case SIOCGIFCONF: { 214 STRUCT_HANDLE(ifconf, ifc); 215 caddr_t ubuf_addr; 216 int ubuf_size; 217 struct ifconf kifc; 218 struct ifreq *ifr, *ptr; 219 int num_ifs; 220 221 STRUCT_SET_HANDLE(ifc, iocp->ioc_flag, 222 (struct ifconf *)(uintptr_t)addr); 223 224 ubuf_size = STRUCT_FGET(ifc, ifc_len); 225 ubuf_addr = STRUCT_FGETP(ifc, ifc_buf); 226 227 err = rds_do_ip_ioctl(SIOCGIFNUM, 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(iocp->ioc_cmd, 236 sizeof (struct ifconf), (caddr_t)&kifc); 237 if (err != 0) { 238 kmem_free(kifc.ifc_buf, kifc.ifc_len); 239 break; 240 } 241 mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE); 242 if (mp1 == NULL) { 243 err = ENOMEM; 244 kmem_free(kifc.ifc_buf, ubuf_size); 245 break; 246 } 247 248 ifr = kifc.ifc_req; 249 ptr = (struct ifreq *)(uintptr_t)mp1->b_rptr; 250 for (; num_ifs > 0 && 251 (int)((uintptr_t)mp1->b_wptr - (uintptr_t)mp1->b_rptr) < 252 ubuf_size; num_ifs--, ifr++) { 253 err = rds_is_ib_interface(ifr->ifr_name); 254 if (err == 0) { 255 ifr->ifr_addr.sa_family = AF_INET_OFFLOAD; 256 bcopy((caddr_t)ifr, ptr, sizeof (struct ifreq)); 257 ptr++; 258 mp1->b_wptr = (uchar_t *)ptr; 259 } else if (err > 0) { 260 break; 261 } else { 262 err = 0; 263 } 264 } 265 266 STRUCT_FSET(ifc, ifc_len, (int)((uintptr_t)mp1->b_wptr - 267 (uintptr_t)mp1->b_rptr)); 268 kmem_free(kifc.ifc_buf, kifc.ifc_len); 269 } 270 break; 271 case SIOCGIFMTU: 272 err = rds_do_ip_ioctl(iocp->ioc_cmd, 273 sizeof (struct ifreq), addr); 274 break; 275 276 case SIOCGIFFLAGS: 277 err = rds_do_ip_ioctl(iocp->ioc_cmd, 278 sizeof (struct ifreq), addr); 279 break; 280 case TI_GETMYNAME: { 281 282 rds_t *rds; 283 STRUCT_HANDLE(strbuf, sb); 284 ipaddr_t v4addr; 285 uint16_t port; 286 int addrlen; 287 sin_t *sin; 288 289 STRUCT_SET_HANDLE(sb, 290 ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag, 291 (void *)(uintptr_t)addr); 292 rds = (rds_t *)q->q_ptr; 293 ASSERT(rds->rds_family == AF_INET_OFFLOAD); 294 addrlen = sizeof (sin_t); 295 v4addr = rds->rds_src; 296 port = rds->rds_port; 297 mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, 298 B_TRUE); 299 if (mp1 == NULL) 300 return; 301 STRUCT_FSET(sb, len, (int)sizeof (sin_t)); 302 sin = (sin_t *)(uintptr_t)mp1->b_rptr; 303 mp1->b_wptr = (uchar_t *)&sin[1]; 304 *sin = sin_null; 305 sin->sin_family = AF_INET_OFFLOAD; 306 sin->sin_addr.s_addr = v4addr; 307 sin->sin_port = port; 308 309 } 310 break; 311 default: 312 err = EOPNOTSUPP; 313 break; 314 } 315 if (err == 0) { 316 mi_copyout(q, mp); 317 return; 318 } 319 done: 320 mi_copy_done(q, mp, err); 321 } 322 323 324 void 325 rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp) 326 { 327 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 328 int copyin_size; 329 330 if (mp->b_cont == NULL) { 331 iocp->ioc_error = EINVAL; 332 mp->b_datap->db_type = M_IOCNAK; 333 iocp->ioc_count = 0; 334 qreply(q, mp); 335 return; 336 } 337 338 switch (iocp->ioc_cmd) { 339 case O_SIOCGIFCONF: 340 case SIOCGIFCONF: 341 if (iocp->ioc_count == TRANSPARENT) 342 copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag); 343 else 344 copyin_size = iocp->ioc_count; 345 break; 346 347 case SIOCGIFNUM: 348 copyin_size = sizeof (int); 349 break; 350 case SIOCGIFFLAGS: 351 case SIOCGIFMTU: 352 copyin_size = sizeof (struct ifreq); 353 break; 354 case TI_GETMYNAME: 355 copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag); 356 break; 357 } 358 mi_copyin(q, mp, NULL, copyin_size); 359 } 360 361 void 362 rds_ioctl(queue_t *q, mblk_t *mp) 363 { 364 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 365 366 367 switch (iocp->ioc_cmd) { 368 case O_SIOCGIFCONF: 369 case SIOCGIFCONF: 370 case SIOCGIFNUM: 371 case SIOCGIFMTU: 372 case SIOCGIFFLAGS: 373 case TI_GETMYNAME: 374 rds_ioctl_copyin_setup(q, mp); 375 break; 376 default: 377 cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n"); 378 miocnak(q, mp, 0, ENOTSUP); 379 break; 380 } 381 } 382 383 boolean_t 384 rds_verify_bind_address(ipaddr_t addr) 385 { 386 int numifs; 387 struct ifconf kifc; 388 struct ifreq *ifr; 389 boolean_t ret = B_FALSE; 390 391 392 if (rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), (caddr_t)&numifs)) { 393 return (ret); 394 } 395 396 kifc.ifc_len = numifs * sizeof (struct ifreq); 397 kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP); 398 399 if (rds_do_ip_ioctl(SIOCGIFCONF, sizeof (struct ifconf), 400 (caddr_t)&kifc)) { 401 goto done; 402 } 403 404 ifr = kifc.ifc_req; 405 for (numifs = kifc.ifc_len / sizeof (struct ifreq); 406 numifs > 0; numifs--, ifr++) { 407 struct sockaddr_in *sin; 408 409 sin = (struct sockaddr_in *)(uintptr_t)&ifr->ifr_addr; 410 if ((sin->sin_addr.s_addr == addr) && 411 (rds_is_ib_interface(ifr->ifr_name) == 0)) { 412 ret = B_TRUE; 413 break; 414 } 415 } 416 417 done: 418 kmem_free(kifc.ifc_buf, kifc.ifc_len); 419 return (ret); 420 } 421