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 ( 110 ddi_parse_dlen(ifname, drv, MAXLINKNAMELEN, &ppa) == DDI_SUCCESS && 111 rds_transport_ops->rds_transport_if_lookup_by_name(drv)); 112 } 113 114 /* 115 * Issue an SIOCGLIFCONF down to IP and return the result in `lifcp'. 116 * lifcp->lifc_buf is dynamically allocated to be *bufsizep bytes. 117 */ 118 static int 119 rds_do_lifconf(struct lifconf *lifcp, uint_t *bufsizep) 120 { 121 int err; 122 int nifs; 123 124 if ((err = rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), &nifs)) != 0) 125 return (err); 126 127 /* 128 * Pad the interface count to account for additional interfaces that 129 * may have been configured between the SIOCGLIFNUM and SIOCGLIFCONF. 130 */ 131 nifs += 4; 132 133 bzero(lifcp, sizeof (struct lifconf)); 134 lifcp->lifc_family = AF_INET; 135 lifcp->lifc_len = *bufsizep = (nifs * sizeof (struct lifreq)); 136 lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_NOSLEEP); 137 if (lifcp->lifc_buf == NULL) 138 return (ENOMEM); 139 140 err = rds_do_ip_ioctl(SIOCGLIFCONF, sizeof (struct lifconf), lifcp); 141 if (err != 0) { 142 kmem_free(lifcp->lifc_buf, *bufsizep); 143 return (err); 144 } 145 return (0); 146 } 147 148 void 149 rds_ioctl_copyin_done(queue_t *q, mblk_t *mp) 150 { 151 void *addr; 152 mblk_t *mp1; 153 int err = 0; 154 struct iocblk *iocp = (void *)mp->b_rptr; 155 156 if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) { 157 err = EPROTO; 158 goto done; 159 } 160 161 addr = mp1->b_rptr; 162 163 switch (iocp->ioc_cmd) { 164 case SIOCGIFNUM: { 165 uint_t bufsize; 166 struct lifconf lifc; 167 struct lifreq *lifrp; 168 int i, nifs, retval = 0; 169 170 if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0) 171 break; 172 173 nifs = lifc.lifc_len / sizeof (struct lifreq); 174 for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) { 175 if (strlen(lifrp->lifr_name) <= IFNAMSIZ && 176 rds_capable_interface(lifrp)) { 177 retval++; 178 } 179 } 180 *((int *)addr) = retval; 181 kmem_free(lifc.lifc_buf, bufsize); 182 break; 183 } 184 185 case O_SIOCGIFCONF: 186 case SIOCGIFCONF: { 187 STRUCT_HANDLE(ifconf, ifc); 188 caddr_t ubuf_addr; 189 int ubuf_size; 190 uint_t bufsize; 191 int i, nifs; 192 struct lifconf lifc; 193 struct lifreq *lifrp; 194 struct ifreq *ifrp; 195 196 STRUCT_SET_HANDLE(ifc, iocp->ioc_flag, (struct ifconf *)addr); 197 ubuf_size = STRUCT_FGET(ifc, ifc_len); 198 ubuf_addr = STRUCT_FGETP(ifc, ifc_buf); 199 200 if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0) 201 break; 202 203 mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE); 204 if (mp1 == NULL) { 205 err = ENOMEM; 206 kmem_free(lifc.lifc_buf, bufsize); 207 break; 208 } 209 210 ifrp = (void *)mp1->b_rptr; 211 nifs = lifc.lifc_len / sizeof (struct lifreq); 212 for (lifrp = lifc.lifc_req, i = 0; i < nifs && 213 MBLKTAIL(mp1) >= sizeof (struct ifreq); i++, lifrp++) { 214 /* 215 * Skip entries that are impossible to return with 216 * SIOCGIFCONF, or not RDS-capable. 217 */ 218 if (strlen(lifrp->lifr_name) > IFNAMSIZ || 219 !rds_capable_interface(lifrp)) { 220 continue; 221 } 222 223 ifrp->ifr_addr = *(struct sockaddr *)&lifrp->lifr_addr; 224 ifrp->ifr_addr.sa_family = AF_INET_OFFLOAD; 225 (void) strlcpy(ifrp->ifr_name, lifrp->lifr_name, 226 IFNAMSIZ); 227 ifrp++; 228 mp1->b_wptr += sizeof (struct ifreq); 229 } 230 231 STRUCT_FSET(ifc, ifc_len, MBLKL(mp1)); 232 kmem_free(lifc.lifc_buf, bufsize); 233 break; 234 } 235 case SIOCGIFMTU: 236 case SIOCGIFFLAGS: 237 err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (struct ifreq), 238 addr); 239 break; 240 241 case TI_GETMYNAME: { 242 rds_t *rds; 243 STRUCT_HANDLE(strbuf, sb); 244 ipaddr_t v4addr; 245 uint16_t port; 246 int addrlen; 247 sin_t *sin; 248 249 STRUCT_SET_HANDLE(sb, 250 ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag, addr); 251 rds = (rds_t *)q->q_ptr; 252 ASSERT(rds->rds_family == AF_INET_OFFLOAD); 253 addrlen = sizeof (sin_t); 254 v4addr = rds->rds_src; 255 port = rds->rds_port; 256 mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, 257 B_TRUE); 258 if (mp1 == NULL) 259 return; 260 STRUCT_FSET(sb, len, (int)sizeof (sin_t)); 261 sin = (sin_t *)(uintptr_t)mp1->b_rptr; 262 mp1->b_wptr = (uchar_t *)&sin[1]; 263 *sin = sin_null; 264 sin->sin_family = AF_INET_OFFLOAD; 265 sin->sin_addr.s_addr = v4addr; 266 sin->sin_port = port; 267 268 } 269 break; 270 default: 271 err = EOPNOTSUPP; 272 break; 273 } 274 if (err == 0) { 275 mi_copyout(q, mp); 276 return; 277 } 278 done: 279 mi_copy_done(q, mp, err); 280 } 281 282 void 283 rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp) 284 { 285 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 286 int copyin_size; 287 288 if (mp->b_cont == NULL) { 289 iocp->ioc_error = EINVAL; 290 mp->b_datap->db_type = M_IOCNAK; 291 iocp->ioc_count = 0; 292 qreply(q, mp); 293 return; 294 } 295 296 switch (iocp->ioc_cmd) { 297 case O_SIOCGIFCONF: 298 case SIOCGIFCONF: 299 if (iocp->ioc_count == TRANSPARENT) 300 copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag); 301 else 302 copyin_size = iocp->ioc_count; 303 break; 304 305 case SIOCGIFNUM: 306 copyin_size = sizeof (int); 307 break; 308 case SIOCGIFFLAGS: 309 case SIOCGIFMTU: 310 copyin_size = sizeof (struct ifreq); 311 break; 312 case TI_GETMYNAME: 313 copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag); 314 break; 315 } 316 mi_copyin(q, mp, NULL, copyin_size); 317 } 318 319 void 320 rds_ioctl(queue_t *q, mblk_t *mp) 321 { 322 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; 323 324 325 switch (iocp->ioc_cmd) { 326 case O_SIOCGIFCONF: 327 case SIOCGIFCONF: 328 case SIOCGIFNUM: 329 case SIOCGIFMTU: 330 case SIOCGIFFLAGS: 331 case TI_GETMYNAME: 332 rds_ioctl_copyin_setup(q, mp); 333 break; 334 default: 335 cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n"); 336 miocnak(q, mp, 0, ENOTSUP); 337 break; 338 } 339 } 340 341 boolean_t 342 rds_verify_bind_address(ipaddr_t addr) 343 { 344 int i, nifs; 345 uint_t bufsize; 346 struct lifconf lifc; 347 struct lifreq *lifrp; 348 struct sockaddr_in *sinp; 349 boolean_t retval = B_FALSE; 350 351 if (rds_do_lifconf(&lifc, &bufsize) != 0) 352 return (B_FALSE); 353 354 nifs = lifc.lifc_len / sizeof (struct lifreq); 355 for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) { 356 sinp = (struct sockaddr_in *)&lifrp->lifr_addr; 357 if (rds_capable_interface(lifrp) && 358 sinp->sin_addr.s_addr == addr) { 359 retval = B_TRUE; 360 break; 361 } 362 } 363 364 kmem_free(lifc.lifc_buf, bufsize); 365 return (retval); 366 } 367