13302Sagiri /*
23302Sagiri * CDDL HEADER START
33302Sagiri *
43302Sagiri * The contents of this file are subject to the terms of the
53302Sagiri * Common Development and Distribution License (the "License").
63302Sagiri * You may not use this file except in compliance with the License.
73302Sagiri *
83302Sagiri * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93302Sagiri * or http://www.opensolaris.org/os/licensing.
103302Sagiri * See the License for the specific language governing permissions
113302Sagiri * and limitations under the License.
123302Sagiri *
133302Sagiri * When distributing Covered Code, include this CDDL HEADER in each
143302Sagiri * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153302Sagiri * If applicable, add the following below this CDDL HEADER, with the
163302Sagiri * fields enclosed by brackets "[]" replaced with your own identifying
173302Sagiri * information: Portions Copyright [yyyy] [name of copyright owner]
183302Sagiri *
193302Sagiri * CDDL HEADER END
203302Sagiri */
213302Sagiri /*
22*11754SKacheong.Poon@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
233302Sagiri * Use is subject to license terms.
243302Sagiri */
253302Sagiri
263302Sagiri #include <sys/sockio.h>
273302Sagiri #include <sys/stream.h>
283302Sagiri #include <sys/errno.h>
293302Sagiri #include <sys/cmn_err.h>
303302Sagiri #include <sys/strsun.h>
313302Sagiri #include <inet/common.h>
323302Sagiri #include <net/if.h>
338485SPeter.Memishian@Sun.COM #include <net/if_types.h>
343302Sagiri #include <inet/mi.h>
353302Sagiri #include <sys/t_kuser.h>
363302Sagiri #include <sys/stropts.h>
373302Sagiri #include <sys/pathname.h>
383302Sagiri #include <sys/kstr.h>
393302Sagiri #include <sys/timod.h>
408485SPeter.Memishian@Sun.COM #include <sys/sunddi.h>
413302Sagiri #include <sys/ib/clients/rds/rds.h>
423302Sagiri #include <sys/ib/clients/rds/rds_transport.h>
433302Sagiri
443302Sagiri /*
453302Sagiri * Just pass the ioctl to IP and the result to the caller.
463302Sagiri */
473302Sagiri int
rds_do_ip_ioctl(int cmd,int len,void * arg)488485SPeter.Memishian@Sun.COM rds_do_ip_ioctl(int cmd, int len, void *arg)
493302Sagiri {
5011185SSean.McEnroe@Sun.COM vnode_t *kkvp, *vp;
513302Sagiri TIUSER *tiptr;
523302Sagiri struct strioctl iocb;
534154Sagiri k_sigset_t smask;
543302Sagiri int err = 0;
553302Sagiri
5611185SSean.McEnroe@Sun.COM if (lookupname("/dev/udp", UIO_SYSSPACE, FOLLOW, NULLVPP, &kkvp) == 0) {
5711185SSean.McEnroe@Sun.COM if (t_kopen((file_t *)NULL, kkvp->v_rdev, FREAD|FWRITE,
583302Sagiri &tiptr, CRED()) == 0) {
593302Sagiri vp = tiptr->fp->f_vnode;
603302Sagiri } else {
6111185SSean.McEnroe@Sun.COM VN_RELE(kkvp);
623302Sagiri return (EPROTO);
633302Sagiri }
643302Sagiri } else {
658485SPeter.Memishian@Sun.COM return (EPROTO);
663302Sagiri }
673302Sagiri
683302Sagiri iocb.ic_cmd = cmd;
693302Sagiri iocb.ic_timout = 0;
703302Sagiri iocb.ic_len = len;
718485SPeter.Memishian@Sun.COM iocb.ic_dp = (caddr_t)arg;
724154Sagiri sigintr(&smask, 0);
733302Sagiri err = kstr_ioctl(vp, I_STR, (intptr_t)&iocb);
744154Sagiri sigunintr(&smask);
753302Sagiri (void) t_kclose(tiptr, 0);
7611185SSean.McEnroe@Sun.COM VN_RELE(kkvp);
773302Sagiri return (err);
783302Sagiri }
793302Sagiri
804154Sagiri /*
818485SPeter.Memishian@Sun.COM * Check if the IP interface named by `lifrp' is RDS-capable.
824154Sagiri */
838485SPeter.Memishian@Sun.COM static boolean_t
rds_capable_interface(struct lifreq * lifrp)848485SPeter.Memishian@Sun.COM rds_capable_interface(struct lifreq *lifrp)
853302Sagiri {
868485SPeter.Memishian@Sun.COM char ifname[LIFNAMSIZ];
878485SPeter.Memishian@Sun.COM char drv[MAXLINKNAMELEN];
888485SPeter.Memishian@Sun.COM uint_t ppa;
898485SPeter.Memishian@Sun.COM char *cp;
903302Sagiri
918485SPeter.Memishian@Sun.COM if (lifrp->lifr_type == IFT_IB)
928485SPeter.Memishian@Sun.COM return (B_TRUE);
933302Sagiri
943302Sagiri /*
958485SPeter.Memishian@Sun.COM * Strip off the logical interface portion before getting
968485SPeter.Memishian@Sun.COM * intimate with the name.
973302Sagiri */
988485SPeter.Memishian@Sun.COM (void) strlcpy(ifname, lifrp->lifr_name, LIFNAMSIZ);
998485SPeter.Memishian@Sun.COM if ((cp = strchr(ifname, ':')) != NULL)
1008485SPeter.Memishian@Sun.COM *cp = '\0';
1013302Sagiri
1028485SPeter.Memishian@Sun.COM if (strcmp("lo0", ifname) == 0) {
1033302Sagiri /*
1048485SPeter.Memishian@Sun.COM * loopback is considered RDS-capable
1053302Sagiri */
1068485SPeter.Memishian@Sun.COM return (B_TRUE);
1073302Sagiri }
1083302Sagiri
1098485SPeter.Memishian@Sun.COM return (ddi_parse(ifname, drv, &ppa) == DDI_SUCCESS &&
1108485SPeter.Memishian@Sun.COM rds_transport_ops->rds_transport_if_lookup_by_name(drv));
1118485SPeter.Memishian@Sun.COM }
1123302Sagiri
1138485SPeter.Memishian@Sun.COM /*
1148485SPeter.Memishian@Sun.COM * Issue an SIOCGLIFCONF down to IP and return the result in `lifcp'.
1158485SPeter.Memishian@Sun.COM * lifcp->lifc_buf is dynamically allocated to be *bufsizep bytes.
1168485SPeter.Memishian@Sun.COM */
1178485SPeter.Memishian@Sun.COM static int
rds_do_lifconf(struct lifconf * lifcp,uint_t * bufsizep)1188485SPeter.Memishian@Sun.COM rds_do_lifconf(struct lifconf *lifcp, uint_t *bufsizep)
1198485SPeter.Memishian@Sun.COM {
1208485SPeter.Memishian@Sun.COM int err;
1218485SPeter.Memishian@Sun.COM int nifs;
1228485SPeter.Memishian@Sun.COM
1238485SPeter.Memishian@Sun.COM if ((err = rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), &nifs)) != 0)
1248485SPeter.Memishian@Sun.COM return (err);
1253302Sagiri
1268485SPeter.Memishian@Sun.COM /*
1278485SPeter.Memishian@Sun.COM * Pad the interface count to account for additional interfaces that
1288485SPeter.Memishian@Sun.COM * may have been configured between the SIOCGLIFNUM and SIOCGLIFCONF.
1298485SPeter.Memishian@Sun.COM */
1308485SPeter.Memishian@Sun.COM nifs += 4;
1318485SPeter.Memishian@Sun.COM
1328485SPeter.Memishian@Sun.COM bzero(lifcp, sizeof (struct lifconf));
1338485SPeter.Memishian@Sun.COM lifcp->lifc_family = AF_INET;
1348485SPeter.Memishian@Sun.COM lifcp->lifc_len = *bufsizep = (nifs * sizeof (struct lifreq));
1358485SPeter.Memishian@Sun.COM lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_NOSLEEP);
1368485SPeter.Memishian@Sun.COM if (lifcp->lifc_buf == NULL)
1378485SPeter.Memishian@Sun.COM return (ENOMEM);
1388485SPeter.Memishian@Sun.COM
1398485SPeter.Memishian@Sun.COM err = rds_do_ip_ioctl(SIOCGLIFCONF, sizeof (struct lifconf), lifcp);
1408485SPeter.Memishian@Sun.COM if (err != 0) {
1418485SPeter.Memishian@Sun.COM kmem_free(lifcp->lifc_buf, *bufsizep);
1428485SPeter.Memishian@Sun.COM return (err);
1433302Sagiri }
1444154Sagiri return (0);
1453302Sagiri }
1463302Sagiri
1473302Sagiri void
rds_ioctl_copyin_done(queue_t * q,mblk_t * mp)1483302Sagiri rds_ioctl_copyin_done(queue_t *q, mblk_t *mp)
1493302Sagiri {
1508485SPeter.Memishian@Sun.COM void *addr;
1513302Sagiri mblk_t *mp1;
1523302Sagiri int err = 0;
1538485SPeter.Memishian@Sun.COM struct iocblk *iocp = (void *)mp->b_rptr;
1543302Sagiri
1553302Sagiri if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) {
1563302Sagiri err = EPROTO;
1573302Sagiri goto done;
1583302Sagiri }
1593302Sagiri
1608485SPeter.Memishian@Sun.COM addr = mp1->b_rptr;
1613302Sagiri
1623302Sagiri switch (iocp->ioc_cmd) {
1638485SPeter.Memishian@Sun.COM case SIOCGIFNUM: {
1648485SPeter.Memishian@Sun.COM uint_t bufsize;
1658485SPeter.Memishian@Sun.COM struct lifconf lifc;
1668485SPeter.Memishian@Sun.COM struct lifreq *lifrp;
1678485SPeter.Memishian@Sun.COM int i, nifs, retval = 0;
1683302Sagiri
1698485SPeter.Memishian@Sun.COM if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0)
1703302Sagiri break;
1713302Sagiri
1728485SPeter.Memishian@Sun.COM nifs = lifc.lifc_len / sizeof (struct lifreq);
1738485SPeter.Memishian@Sun.COM for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) {
1748485SPeter.Memishian@Sun.COM if (strlen(lifrp->lifr_name) <= IFNAMSIZ &&
1758485SPeter.Memishian@Sun.COM rds_capable_interface(lifrp)) {
1768485SPeter.Memishian@Sun.COM retval++;
1778485SPeter.Memishian@Sun.COM }
1783302Sagiri }
1798485SPeter.Memishian@Sun.COM *((int *)addr) = retval;
1808485SPeter.Memishian@Sun.COM kmem_free(lifc.lifc_buf, bufsize);
1818485SPeter.Memishian@Sun.COM break;
1823302Sagiri }
1833302Sagiri
1843302Sagiri case O_SIOCGIFCONF:
1853302Sagiri case SIOCGIFCONF: {
1863302Sagiri STRUCT_HANDLE(ifconf, ifc);
1873302Sagiri caddr_t ubuf_addr;
1883302Sagiri int ubuf_size;
1898485SPeter.Memishian@Sun.COM uint_t bufsize;
1908485SPeter.Memishian@Sun.COM int i, nifs;
1918485SPeter.Memishian@Sun.COM struct lifconf lifc;
1928485SPeter.Memishian@Sun.COM struct lifreq *lifrp;
1938485SPeter.Memishian@Sun.COM struct ifreq *ifrp;
1943302Sagiri
1958485SPeter.Memishian@Sun.COM STRUCT_SET_HANDLE(ifc, iocp->ioc_flag, (struct ifconf *)addr);
1963302Sagiri ubuf_size = STRUCT_FGET(ifc, ifc_len);
1973302Sagiri ubuf_addr = STRUCT_FGETP(ifc, ifc_buf);
1983302Sagiri
1998485SPeter.Memishian@Sun.COM if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0)
2003302Sagiri break;
2013302Sagiri
2023302Sagiri mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE);
2033302Sagiri if (mp1 == NULL) {
2043302Sagiri err = ENOMEM;
2058485SPeter.Memishian@Sun.COM kmem_free(lifc.lifc_buf, bufsize);
2063302Sagiri break;
2073302Sagiri }
2083302Sagiri
2098485SPeter.Memishian@Sun.COM ifrp = (void *)mp1->b_rptr;
2108485SPeter.Memishian@Sun.COM nifs = lifc.lifc_len / sizeof (struct lifreq);
2118485SPeter.Memishian@Sun.COM for (lifrp = lifc.lifc_req, i = 0; i < nifs &&
2128485SPeter.Memishian@Sun.COM MBLKTAIL(mp1) >= sizeof (struct ifreq); i++, lifrp++) {
2138485SPeter.Memishian@Sun.COM /*
2148485SPeter.Memishian@Sun.COM * Skip entries that are impossible to return with
2158485SPeter.Memishian@Sun.COM * SIOCGIFCONF, or not RDS-capable.
2168485SPeter.Memishian@Sun.COM */
2178485SPeter.Memishian@Sun.COM if (strlen(lifrp->lifr_name) > IFNAMSIZ ||
2188485SPeter.Memishian@Sun.COM !rds_capable_interface(lifrp)) {
2198485SPeter.Memishian@Sun.COM continue;
2203302Sagiri }
2218485SPeter.Memishian@Sun.COM
2228485SPeter.Memishian@Sun.COM ifrp->ifr_addr = *(struct sockaddr *)&lifrp->lifr_addr;
2238485SPeter.Memishian@Sun.COM ifrp->ifr_addr.sa_family = AF_INET_OFFLOAD;
2248485SPeter.Memishian@Sun.COM (void) strlcpy(ifrp->ifr_name, lifrp->lifr_name,
2258485SPeter.Memishian@Sun.COM IFNAMSIZ);
2268485SPeter.Memishian@Sun.COM ifrp++;
2278485SPeter.Memishian@Sun.COM mp1->b_wptr += sizeof (struct ifreq);
2283302Sagiri }
2293302Sagiri
2308485SPeter.Memishian@Sun.COM STRUCT_FSET(ifc, ifc_len, MBLKL(mp1));
2318485SPeter.Memishian@Sun.COM kmem_free(lifc.lifc_buf, bufsize);
2328485SPeter.Memishian@Sun.COM break;
2333302Sagiri }
2343302Sagiri case SIOCGIFMTU:
2358485SPeter.Memishian@Sun.COM case SIOCGIFFLAGS:
2368485SPeter.Memishian@Sun.COM err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (struct ifreq),
2378485SPeter.Memishian@Sun.COM addr);
2383302Sagiri break;
2393302Sagiri
2403302Sagiri case TI_GETMYNAME: {
2413302Sagiri rds_t *rds;
2423302Sagiri STRUCT_HANDLE(strbuf, sb);
2433302Sagiri ipaddr_t v4addr;
2443302Sagiri uint16_t port;
2453302Sagiri int addrlen;
2463302Sagiri sin_t *sin;
2473302Sagiri
2483302Sagiri STRUCT_SET_HANDLE(sb,
2498485SPeter.Memishian@Sun.COM ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag, addr);
2503302Sagiri rds = (rds_t *)q->q_ptr;
2513302Sagiri ASSERT(rds->rds_family == AF_INET_OFFLOAD);
2523302Sagiri addrlen = sizeof (sin_t);
2533302Sagiri v4addr = rds->rds_src;
2543302Sagiri port = rds->rds_port;
2553302Sagiri mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen,
2563302Sagiri B_TRUE);
2573302Sagiri if (mp1 == NULL)
2583302Sagiri return;
2593302Sagiri STRUCT_FSET(sb, len, (int)sizeof (sin_t));
2603302Sagiri sin = (sin_t *)(uintptr_t)mp1->b_rptr;
2613302Sagiri mp1->b_wptr = (uchar_t *)&sin[1];
2623302Sagiri *sin = sin_null;
2633302Sagiri sin->sin_family = AF_INET_OFFLOAD;
2643302Sagiri sin->sin_addr.s_addr = v4addr;
2653302Sagiri sin->sin_port = port;
2663302Sagiri
2673302Sagiri }
2683302Sagiri break;
2693302Sagiri default:
2703302Sagiri err = EOPNOTSUPP;
2713302Sagiri break;
2723302Sagiri }
2733302Sagiri if (err == 0) {
2743302Sagiri mi_copyout(q, mp);
2753302Sagiri return;
2763302Sagiri }
2773302Sagiri done:
2783302Sagiri mi_copy_done(q, mp, err);
2793302Sagiri }
2803302Sagiri
2813302Sagiri void
rds_ioctl_copyin_setup(queue_t * q,mblk_t * mp)2823302Sagiri rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp)
2833302Sagiri {
2843302Sagiri struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
2853302Sagiri int copyin_size;
2863302Sagiri
2873302Sagiri if (mp->b_cont == NULL) {
2883302Sagiri iocp->ioc_error = EINVAL;
2893302Sagiri mp->b_datap->db_type = M_IOCNAK;
2903302Sagiri iocp->ioc_count = 0;
2913302Sagiri qreply(q, mp);
2923302Sagiri return;
2933302Sagiri }
2943302Sagiri
2953302Sagiri switch (iocp->ioc_cmd) {
2963302Sagiri case O_SIOCGIFCONF:
2973302Sagiri case SIOCGIFCONF:
2983302Sagiri if (iocp->ioc_count == TRANSPARENT)
2993302Sagiri copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag);
3003302Sagiri else
3013302Sagiri copyin_size = iocp->ioc_count;
3023302Sagiri break;
3033302Sagiri
3043302Sagiri case SIOCGIFNUM:
3053302Sagiri copyin_size = sizeof (int);
3063302Sagiri break;
3073302Sagiri case SIOCGIFFLAGS:
3083302Sagiri case SIOCGIFMTU:
3093302Sagiri copyin_size = sizeof (struct ifreq);
3103302Sagiri break;
3113302Sagiri case TI_GETMYNAME:
3123302Sagiri copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag);
3133302Sagiri break;
3143302Sagiri }
3153302Sagiri mi_copyin(q, mp, NULL, copyin_size);
3163302Sagiri }
3173302Sagiri
3183302Sagiri void
rds_ioctl(queue_t * q,mblk_t * mp)3193302Sagiri rds_ioctl(queue_t *q, mblk_t *mp)
3203302Sagiri {
3213302Sagiri struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
3223302Sagiri
3233302Sagiri
3243302Sagiri switch (iocp->ioc_cmd) {
3253302Sagiri case O_SIOCGIFCONF:
3263302Sagiri case SIOCGIFCONF:
3273302Sagiri case SIOCGIFNUM:
3283302Sagiri case SIOCGIFMTU:
3293302Sagiri case SIOCGIFFLAGS:
3303302Sagiri case TI_GETMYNAME:
3313302Sagiri rds_ioctl_copyin_setup(q, mp);
3323302Sagiri break;
3333302Sagiri default:
3343302Sagiri cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n");
3353302Sagiri miocnak(q, mp, 0, ENOTSUP);
3363302Sagiri break;
3373302Sagiri }
3383302Sagiri }
3393302Sagiri
3403302Sagiri boolean_t
rds_verify_bind_address(ipaddr_t addr)3413302Sagiri rds_verify_bind_address(ipaddr_t addr)
3423302Sagiri {
3438485SPeter.Memishian@Sun.COM int i, nifs;
3448485SPeter.Memishian@Sun.COM uint_t bufsize;
3458485SPeter.Memishian@Sun.COM struct lifconf lifc;
3468485SPeter.Memishian@Sun.COM struct lifreq *lifrp;
3478485SPeter.Memishian@Sun.COM struct sockaddr_in *sinp;
3488485SPeter.Memishian@Sun.COM boolean_t retval = B_FALSE;
3493302Sagiri
3508485SPeter.Memishian@Sun.COM if (rds_do_lifconf(&lifc, &bufsize) != 0)
3518485SPeter.Memishian@Sun.COM return (B_FALSE);
3523302Sagiri
3538485SPeter.Memishian@Sun.COM nifs = lifc.lifc_len / sizeof (struct lifreq);
3548485SPeter.Memishian@Sun.COM for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) {
3558485SPeter.Memishian@Sun.COM sinp = (struct sockaddr_in *)&lifrp->lifr_addr;
3568485SPeter.Memishian@Sun.COM if (rds_capable_interface(lifrp) &&
3578485SPeter.Memishian@Sun.COM sinp->sin_addr.s_addr == addr) {
3588485SPeter.Memishian@Sun.COM retval = B_TRUE;
3598485SPeter.Memishian@Sun.COM break;
3603302Sagiri }
3613302Sagiri }
3623302Sagiri
3638485SPeter.Memishian@Sun.COM kmem_free(lifc.lifc_buf, bufsize);
3648485SPeter.Memishian@Sun.COM return (retval);
3653302Sagiri }
366