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