xref: /onnv-gate/usr/src/uts/common/io/ib/clients/rds/rds_ioctl.c (revision 11754:251da8f4caaa)
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