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/sockio.h> 27 #include <sys/stream.h> 28 #include <sys/errno.h> 29 #include <sys/cmn_err.h> 30 #include <sys/strsun.h> 31 #include <inet/common.h> 32 #include <net/if.h> 33 #include <net/if_types.h> 34 #include <inet/mi.h> 35 #include <sys/t_kuser.h> 36 #include <sys/stropts.h> 37 #include <sys/pathname.h> 38 #include <sys/kstr.h> 39 #include <sys/timod.h> 40 #include <sys/sunddi.h> 41 #include <sys/ib/clients/rds/rds.h> 42 #include <sys/ib/clients/rds/rds_transport.h> 43 44 /* 45 * Just pass the ioctl to IP and the result to the caller. 46 */ 47 int 48 rds_do_ip_ioctl(int cmd, int len, void *arg) 49 { 50 vnode_t *kkvp, *vp; 51 TIUSER *tiptr; 52 struct strioctl iocb; 53 k_sigset_t smask; 54 int err = 0; 55 56 if (lookupname("/dev/udp", UIO_SYSSPACE, FOLLOW, NULLVPP, &kkvp) == 0) { 57 if (t_kopen((file_t *)NULL, kkvp->v_rdev, FREAD|FWRITE, 58 &tiptr, CRED()) == 0) { 59 vp = tiptr->fp->f_vnode; 60 } else { 61 VN_RELE(kkvp); 62 return (EPROTO); 63 } 64 } else { 65 return (EPROTO); 66 } 67 68 iocb.ic_cmd = cmd; 69 iocb.ic_timout = 0; 70 iocb.ic_len = len; 71 iocb.ic_dp = (caddr_t)arg; 72 sigintr(&smask, 0); 73 err = kstr_ioctl(vp, I_STR, (intptr_t)&iocb); 74 sigunintr(&smask); 75 (void) t_kclose(tiptr, 0); 76 VN_RELE(kkvp); 77 return (err); 78 } 79 80 /* 81 * Check if the IP interface named by `lifrp' is RDS-capable. 82 */ 83 static boolean_t 84 rds_capable_interface(struct lifreq *lifrp) 85 { 86 char ifname[LIFNAMSIZ]; 87 char drv[MAXLINKNAMELEN]; 88 uint_t ppa; 89 char *cp; 90 91 if (lifrp->lifr_type == IFT_IB) 92 return (B_TRUE); 93 94 /* 95 * Strip off the logical interface portion before getting 96 * intimate with the name. 97 */ 98 (void) strlcpy(ifname, lifrp->lifr_name, LIFNAMSIZ); 99 if ((cp = strchr(ifname, ':')) != NULL) 100 *cp = '\0'; 101 102 if (strcmp("lo0", ifname) == 0) { 103 /* 104 * loopback is considered RDS-capable 105 */ 106 return (B_TRUE); 107 } 108 109 return (ddi_parse(ifname, drv, &ppa) == DDI_SUCCESS && 110 rds_transport_ops->rds_transport_if_lookup_by_name(drv)); 111 } 112 113 /* 114 * Issue an SIOCGLIFCONF down to IP and return the result in `lifcp'. 115 * lifcp->lifc_buf is dynamically allocated to be *bufsizep bytes. 116 */ 117 static int 118 rds_do_lifconf(struct lifconf *lifcp, uint_t *bufsizep) 119 { 120 int err; 121 int nifs; 122 123 if ((err = rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), &nifs)) != 0) 124 return (err); 125 126 /* 127 * Pad the interface count to account for additional interfaces that 128 * may have been configured between the SIOCGLIFNUM and SIOCGLIFCONF. 129 */ 130 nifs += 4; 131 132 bzero(lifcp, sizeof (struct lifconf)); 133 lifcp->lifc_family = AF_INET; 134 lifcp->lifc_len = *bufsizep = (nifs * sizeof (struct lifreq)); 135 lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_NOSLEEP); 136 if (lifcp->lifc_buf == NULL) 137 return (ENOMEM); 138 139 err = rds_do_ip_ioctl(SIOCGLIFCONF, sizeof (struct lifconf), lifcp); 140 if (err != 0) { 141 kmem_free(lifcp->lifc_buf, *bufsizep); 142 return (err); 143 } 144 return (0); 145 } 146 147 void 148 rds_ioctl_copyin_done(queue_t *q, mblk_t *mp) 149 { 150 void *addr; 151 mblk_t *mp1; 152 int err = 0; 153 struct iocblk *iocp = (void *)mp->b_rptr; 154 155 if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) { 156 err = EPROTO; 157 goto done; 158 } 159 160 addr = mp1->b_rptr; 161 162 switch (iocp->ioc_cmd) { 163 case SIOCGIFNUM: { 164 uint_t bufsize; 165 struct lifconf lifc; 166 struct lifreq *lifrp; 167 int i, nifs, retval = 0; 168 169 if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0) 170 break; 171 172 nifs = lifc.lifc_len / sizeof (struct lifreq); 173 for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) { 174 if (strlen(lifrp->lifr_name) <= IFNAMSIZ && 175 rds_capable_interface(lifrp)) { 176 retval++; 177 } 178 } 179 *((int *)addr) = retval; 180 kmem_free(lifc.lifc_buf, bufsize); 181 break; 182 } 183 184 case O_SIOCGIFCONF: 185 case SIOCGIFCONF: { 186 STRUCT_HANDLE(ifconf, ifc); 187 caddr_t ubuf_addr; 188 int ubuf_size; 189 uint_t bufsize; 190 int i, nifs; 191 struct lifconf lifc; 192 struct lifreq *lifrp; 193 struct ifreq *ifrp; 194 195 STRUCT_SET_HANDLE(ifc, iocp->ioc_flag, (struct ifconf *)addr); 196 ubuf_size = STRUCT_FGET(ifc, ifc_len); 197 ubuf_addr = STRUCT_FGETP(ifc, ifc_buf); 198 199 if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0) 200 break; 201 202 mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE); 203 if (mp1 == NULL) { 204 err = ENOMEM; 205 kmem_free(lifc.lifc_buf, bufsize); 206 break; 207 } 208 209 ifrp = (void *)mp1->b_rptr; 210 nifs = lifc.lifc_len / sizeof (struct lifreq); 211 for (lifrp = lifc.lifc_req, i = 0; i < nifs && 212 MBLKTAIL(mp1) >= sizeof (struct ifreq); i++, lifrp++) { 213 /* 214 * Skip entries that are impossible to return with 215 * SIOCGIFCONF, or not RDS-capable. 216 */ 217 if (strlen(lifrp->lifr_name) > IFNAMSIZ || 218 !rds_capable_interface(lifrp)) { 219 continue; 220 } 221 222 ifrp->ifr_addr = *(struct sockaddr *)&lifrp->lifr_addr; 223 ifrp->ifr_addr.sa_family = AF_INET_OFFLOAD; 224 (void) strlcpy(ifrp->ifr_name, lifrp->lifr_name, 225 IFNAMSIZ); 226 ifrp++; 227 mp1->b_wptr += sizeof (struct ifreq); 228 } 229 230 STRUCT_FSET(ifc, ifc_len, MBLKL(mp1)); 231 kmem_free(lifc.lifc_buf, bufsize); 232 break; 233 } 234 case SIOCGIFMTU: 235 case SIOCGIFFLAGS: 236 err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (struct ifreq), 237 addr); 238 break; 239 240 case TI_GETMYNAME: { 241 rds_t *rds; 242 STRUCT_HANDLE(strbuf, sb); 243 ipaddr_t v4addr; 244 uint16_t port; 245 int addrlen; 246 sin_t *sin; 247 248 STRUCT_SET_HANDLE(sb, 249 ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag, addr); 250 rds = (rds_t *)q->q_ptr; 251 ASSERT(rds->rds_family == AF_INET_OFFLOAD); 252 addrlen = sizeof (sin_t); 253 v4addr = rds->rds_src; 254 port = rds->rds_port; 255 mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, 256 B_TRUE); 257 if (mp1 == NULL) 258 return; 259 STRUCT_FSET(sb, len, (int)sizeof (sin_t)); 260 sin = (sin_t *)(uintptr_t)mp1->b_rptr; 261 mp1->b_wptr = (uchar_t *)&sin[1]; 262 *sin = sin_null; 263 sin->sin_family = AF_INET_OFFLOAD; 264 sin->sin_addr.s_addr = v4addr; 265 sin->sin_port = port; 266 267 } 268 break; 269 default: 270 err = EOPNOTSUPP; 271 break; 272 } 273 if (err == 0) { 274 mi_copyout(q, mp); 275 return; 276 } 277 done: 278 mi_copy_done(q, mp, err); 279 } 280 281 void 282 rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp) 283 { 284 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 285 int copyin_size; 286 287 if (mp->b_cont == NULL) { 288 iocp->ioc_error = EINVAL; 289 mp->b_datap->db_type = M_IOCNAK; 290 iocp->ioc_count = 0; 291 qreply(q, mp); 292 return; 293 } 294 295 switch (iocp->ioc_cmd) { 296 case O_SIOCGIFCONF: 297 case SIOCGIFCONF: 298 if (iocp->ioc_count == TRANSPARENT) 299 copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag); 300 else 301 copyin_size = iocp->ioc_count; 302 break; 303 304 case SIOCGIFNUM: 305 copyin_size = sizeof (int); 306 break; 307 case SIOCGIFFLAGS: 308 case SIOCGIFMTU: 309 copyin_size = sizeof (struct ifreq); 310 break; 311 case TI_GETMYNAME: 312 copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag); 313 break; 314 } 315 mi_copyin(q, mp, NULL, copyin_size); 316 } 317 318 void 319 rds_ioctl(queue_t *q, mblk_t *mp) 320 { 321 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 322 323 324 switch (iocp->ioc_cmd) { 325 case O_SIOCGIFCONF: 326 case SIOCGIFCONF: 327 case SIOCGIFNUM: 328 case SIOCGIFMTU: 329 case SIOCGIFFLAGS: 330 case TI_GETMYNAME: 331 rds_ioctl_copyin_setup(q, mp); 332 break; 333 default: 334 cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n"); 335 miocnak(q, mp, 0, ENOTSUP); 336 break; 337 } 338 } 339 340 boolean_t 341 rds_verify_bind_address(ipaddr_t addr) 342 { 343 int i, nifs; 344 uint_t bufsize; 345 struct lifconf lifc; 346 struct lifreq *lifrp; 347 struct sockaddr_in *sinp; 348 boolean_t retval = B_FALSE; 349 350 if (rds_do_lifconf(&lifc, &bufsize) != 0) 351 return (B_FALSE); 352 353 nifs = lifc.lifc_len / sizeof (struct lifreq); 354 for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) { 355 sinp = (struct sockaddr_in *)&lifrp->lifr_addr; 356 if (rds_capable_interface(lifrp) && 357 sinp->sin_addr.s_addr == addr) { 358 retval = B_TRUE; 359 break; 360 } 361 } 362 363 kmem_free(lifc.lifc_buf, bufsize); 364 return (retval); 365 } 366