10f1702c5SYu Xiangning /*
20f1702c5SYu Xiangning  * CDDL HEADER START
30f1702c5SYu Xiangning  *
40f1702c5SYu Xiangning  * The contents of this file are subject to the terms of the
50f1702c5SYu Xiangning  * Common Development and Distribution License (the "License").
60f1702c5SYu Xiangning  * You may not use this file except in compliance with the License.
70f1702c5SYu Xiangning  *
80f1702c5SYu Xiangning  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90f1702c5SYu Xiangning  * or http://www.opensolaris.org/os/licensing.
100f1702c5SYu Xiangning  * See the License for the specific language governing permissions
110f1702c5SYu Xiangning  * and limitations under the License.
120f1702c5SYu Xiangning  *
130f1702c5SYu Xiangning  * When distributing Covered Code, include this CDDL HEADER in each
140f1702c5SYu Xiangning  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150f1702c5SYu Xiangning  * If applicable, add the following below this CDDL HEADER, with the
160f1702c5SYu Xiangning  * fields enclosed by brackets "[]" replaced with your own identifying
170f1702c5SYu Xiangning  * information: Portions Copyright [yyyy] [name of copyright owner]
180f1702c5SYu Xiangning  *
190f1702c5SYu Xiangning  * CDDL HEADER END
200f1702c5SYu Xiangning  */
210f1702c5SYu Xiangning 
220f1702c5SYu Xiangning /*
23bfcb55b8SRao Shoaib  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240f1702c5SYu Xiangning  * Use is subject to license terms.
250f1702c5SYu Xiangning  */
260f1702c5SYu Xiangning 
270f1702c5SYu Xiangning #include <sys/types.h>
280f1702c5SYu Xiangning #include <inet/ip.h>
290f1702c5SYu Xiangning #include <inet/ip_impl.h>
300f1702c5SYu Xiangning #include <inet/ipclassifier.h>
310f1702c5SYu Xiangning #include <inet/proto_set.h>
320f1702c5SYu Xiangning #include <sys/stream.h>
330f1702c5SYu Xiangning #include <sys/strsubr.h>
340f1702c5SYu Xiangning #include <sys/strsun.h>
350f1702c5SYu Xiangning #include <sys/cmn_err.h>
360f1702c5SYu Xiangning #include <sys/t_kuser.h>
370f1702c5SYu Xiangning #include <sys/tihdr.h>
380f1702c5SYu Xiangning #include <sys/pathname.h>
390f1702c5SYu Xiangning #include <sys/sockio.h>
400f1702c5SYu Xiangning #include <sys/vmem.h>
410f1702c5SYu Xiangning #include <sys/disp.h>
420f1702c5SYu Xiangning 
438a06b3d6SToomas Soome int ip_helper_wput(queue_t *q, mblk_t *mp);
440f1702c5SYu Xiangning 
455e1743f0SToomas Soome static int ip_helper_stream_close(queue_t *, int, cred_t *);
460f1702c5SYu Xiangning 
470f1702c5SYu Xiangning static struct module_info ip_helper_stream_info =  {
480f1702c5SYu Xiangning 	0, "iphelper", IP_MOD_MINPSZ, IP_MOD_MAXPSZ, IP_MOD_HIWAT, IP_MOD_LOWAT
490f1702c5SYu Xiangning };
500f1702c5SYu Xiangning 
510f1702c5SYu Xiangning static struct qinit ip_helper_stream_rinit = {
520f1702c5SYu Xiangning 	NULL, NULL, NULL, ip_helper_stream_close, NULL,
530f1702c5SYu Xiangning 	&ip_helper_stream_info, NULL
540f1702c5SYu Xiangning };
550f1702c5SYu Xiangning 
560f1702c5SYu Xiangning static struct qinit ip_helper_stream_winit = {
578a06b3d6SToomas Soome 	ip_helper_wput, ip_wsrv, NULL, NULL, NULL,
580f1702c5SYu Xiangning 	&ip_helper_stream_info, NULL, NULL, NULL, STRUIOT_NONE
590f1702c5SYu Xiangning };
600f1702c5SYu Xiangning 
610f1702c5SYu Xiangning /*
620f1702c5SYu Xiangning  * set the q_ptr of the 'q' to the conn_t pointer passed in
630f1702c5SYu Xiangning  */
640f1702c5SYu Xiangning static void
ip_helper_share_conn(queue_t * q,mblk_t * mp,cred_t * crp)65bfcb55b8SRao Shoaib ip_helper_share_conn(queue_t *q, mblk_t *mp, cred_t *crp)
660f1702c5SYu Xiangning {
67bd670b35SErik Nordmark 	conn_t *connp = *((conn_t **)mp->b_cont->b_rptr);
68bd670b35SErik Nordmark 
69bfcb55b8SRao Shoaib 	/*
70bfcb55b8SRao Shoaib 	 * This operation is allowed only on helper streams with kcred
71bfcb55b8SRao Shoaib 	 */
72bfcb55b8SRao Shoaib 
73bfcb55b8SRao Shoaib 	if (kcred != crp || msgdsize(mp->b_cont) != sizeof (void *)) {
74bfcb55b8SRao Shoaib 		miocnak(q, mp, 0, EINVAL);
75bfcb55b8SRao Shoaib 		return;
76bfcb55b8SRao Shoaib 	}
77bfcb55b8SRao Shoaib 
78bd670b35SErik Nordmark 	connp->conn_helper_info->iphs_minfo = q->q_ptr;
79bd670b35SErik Nordmark 	connp->conn_helper_info->iphs_rq = RD(q);
80bd670b35SErik Nordmark 	connp->conn_helper_info->iphs_wq = WR(q);
81bd670b35SErik Nordmark 	WR(q)->q_ptr = RD(q)->q_ptr = (void *)connp;
82bd670b35SErik Nordmark 	connp->conn_rq = RD(q);
83bd670b35SErik Nordmark 	connp->conn_wq = WR(q);
840f1702c5SYu Xiangning 	miocack(q, mp, 0, 0);
850f1702c5SYu Xiangning }
860f1702c5SYu Xiangning 
878a06b3d6SToomas Soome int
ip_helper_wput(queue_t * q,mblk_t * mp)880f1702c5SYu Xiangning ip_helper_wput(queue_t *q, mblk_t *mp)
890f1702c5SYu Xiangning {
900f1702c5SYu Xiangning 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
910f1702c5SYu Xiangning 	if (DB_TYPE(mp) == M_IOCTL &&
920f1702c5SYu Xiangning 	    iocp->ioc_cmd == SIOCSQPTR) {
93bfcb55b8SRao Shoaib 		ip_helper_share_conn(q, mp, iocp->ioc_cr);
940f1702c5SYu Xiangning 	} else {
95bd670b35SErik Nordmark 		/* We only handle ioctl related messages here */
96bd670b35SErik Nordmark 		ASSERT(DB_TYPE(mp) != M_DATA);
97bd670b35SErik Nordmark 		ip_wput_nondata(q, mp);
980f1702c5SYu Xiangning 	}
998a06b3d6SToomas Soome 	return (0);
1000f1702c5SYu Xiangning }
1010f1702c5SYu Xiangning 
102bd670b35SErik Nordmark /* ARGSUSED3 */
1030f1702c5SYu Xiangning int
ip_helper_stream_setup(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp,boolean_t isv6)1040f1702c5SYu Xiangning ip_helper_stream_setup(queue_t *q, dev_t *devp, int flag, int sflag,
1050f1702c5SYu Xiangning     cred_t *credp, boolean_t isv6)
1060f1702c5SYu Xiangning {
1070f1702c5SYu Xiangning 	major_t			maj;
1080f1702c5SYu Xiangning 	ip_helper_minfo_t	*ip_minfop;
1090f1702c5SYu Xiangning 
1100f1702c5SYu Xiangning 	ASSERT((flag & ~(FKLYR)) == IP_HELPER_STR);
1110f1702c5SYu Xiangning 
1120f1702c5SYu Xiangning 	ASSERT(RD(q) == q);
1130f1702c5SYu Xiangning 
114bd670b35SErik Nordmark 	ip_minfop = kmem_alloc(sizeof (ip_helper_minfo_t), KM_SLEEP);
115bd670b35SErik Nordmark 	ASSERT(ip_minfop != NULL);
1160f1702c5SYu Xiangning 
1170f1702c5SYu Xiangning 	ip_minfop->ip_minfo_dev = 0;
1180f1702c5SYu Xiangning 	ip_minfop->ip_minfo_arena = NULL;
1190f1702c5SYu Xiangning 
1200f1702c5SYu Xiangning 	/*
1210f1702c5SYu Xiangning 	 * Clone the device, allocate minor device number
1220f1702c5SYu Xiangning 	 */
1230f1702c5SYu Xiangning 	if (ip_minor_arena_la != NULL)
1240f1702c5SYu Xiangning 		ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_la);
1250f1702c5SYu Xiangning 
1260f1702c5SYu Xiangning 	if (ip_minfop->ip_minfo_dev == 0) {
1270f1702c5SYu Xiangning 		/*
1280f1702c5SYu Xiangning 		 * numbers in the large arena are exhausted
1290f1702c5SYu Xiangning 		 * Try small arena.
1300f1702c5SYu Xiangning 		 */
1310f1702c5SYu Xiangning 		ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_sa);
1320f1702c5SYu Xiangning 		if (ip_minfop->ip_minfo_dev == 0) {
133*c186a853SToomas Soome 			kmem_free(ip_minfop, sizeof (ip_helper_minfo_t));
1340f1702c5SYu Xiangning 			return (EBUSY);
1350f1702c5SYu Xiangning 		}
1360f1702c5SYu Xiangning 		ip_minfop->ip_minfo_arena = ip_minor_arena_sa;
1370f1702c5SYu Xiangning 	} else {
1380f1702c5SYu Xiangning 		ip_minfop->ip_minfo_arena = ip_minor_arena_la;
1390f1702c5SYu Xiangning 	}
1400f1702c5SYu Xiangning 
1410f1702c5SYu Xiangning 
1420f1702c5SYu Xiangning 	ASSERT(ip_minfop->ip_minfo_dev != 0);
1430f1702c5SYu Xiangning 	ASSERT(ip_minfop->ip_minfo_arena != NULL);
1440f1702c5SYu Xiangning 
1450f1702c5SYu Xiangning 	RD(q)->q_ptr = WR(q)->q_ptr = ip_minfop;
1460f1702c5SYu Xiangning 
1470f1702c5SYu Xiangning 	maj = getemajor(*devp);
1480f1702c5SYu Xiangning 	*devp = makedevice(maj, (ulong_t)(ip_minfop->ip_minfo_dev));
1490f1702c5SYu Xiangning 
1500f1702c5SYu Xiangning 	q->q_qinfo = &ip_helper_stream_rinit;
1510f1702c5SYu Xiangning 	WR(q)->q_qinfo = &ip_helper_stream_winit;
1520f1702c5SYu Xiangning 	qprocson(q);
1530f1702c5SYu Xiangning 	return (0);
1540f1702c5SYu Xiangning }
1550f1702c5SYu Xiangning 
1565e1743f0SToomas Soome /* ARGSUSED */
1570f1702c5SYu Xiangning static int
ip_helper_stream_close(queue_t * q,int flag __unused,cred_t * credp __unused)1585e1743f0SToomas Soome ip_helper_stream_close(queue_t *q, int flag __unused, cred_t *credp __unused)
1590f1702c5SYu Xiangning {
1600f1702c5SYu Xiangning 	ip_helper_minfo_t *ip_minfop;
1610f1702c5SYu Xiangning 
1620f1702c5SYu Xiangning 	qprocsoff(q);
1630f1702c5SYu Xiangning 	ip_minfop = (q)->q_ptr;
1640f1702c5SYu Xiangning 	inet_minor_free(ip_minfop->ip_minfo_arena,
1650f1702c5SYu Xiangning 	    ip_minfop->ip_minfo_dev);
1660f1702c5SYu Xiangning 	kmem_free(ip_minfop, sizeof (ip_helper_minfo_t));
1670f1702c5SYu Xiangning 	RD(q)->q_ptr = NULL;
1680f1702c5SYu Xiangning 	WR(q)->q_ptr = NULL;
1690f1702c5SYu Xiangning 	return (0);
1700f1702c5SYu Xiangning }
1710f1702c5SYu Xiangning 
1720f1702c5SYu Xiangning /*
1730f1702c5SYu Xiangning  * Public interface for creating an IP stream with shared conn_t
174bd670b35SErik Nordmark  * Handles multiple callers in parallel by using conn_lock.
175bd670b35SErik Nordmark  * Note that we allocate the helper stream without any locks, which means
176bd670b35SErik Nordmark  * we might need to free it if we had two threads doing this concurrently
177bd670b35SErik Nordmark  * for the conn_t.
1780f1702c5SYu Xiangning  */
1790f1702c5SYu Xiangning int
ip_create_helper_stream(conn_t * connp,ldi_ident_t li)1800f1702c5SYu Xiangning ip_create_helper_stream(conn_t *connp, ldi_ident_t li)
1810f1702c5SYu Xiangning {
182bd670b35SErik Nordmark 	ip_helper_stream_info_t *helper;
1830f1702c5SYu Xiangning 	int	error;
1840f1702c5SYu Xiangning 	int	ret;
1850f1702c5SYu Xiangning 
1860f1702c5SYu Xiangning 	ASSERT(!servicing_interrupt());
1870f1702c5SYu Xiangning 
188bd670b35SErik Nordmark 	if (connp->conn_helper_info != NULL) {
189bd670b35SErik Nordmark 		/* Already allocated */
190bd670b35SErik Nordmark 		return (0);
1910f1702c5SYu Xiangning 	}
1920f1702c5SYu Xiangning 
193bd670b35SErik Nordmark 	error = 0;
194bd670b35SErik Nordmark 	helper = kmem_alloc(sizeof (ip_helper_stream_info_t), KM_SLEEP);
1950f1702c5SYu Xiangning 
1960f1702c5SYu Xiangning 	/*
197bd670b35SErik Nordmark 	 * open ip device via the layered interface.
198bd670b35SErik Nordmark 	 * pass in kcred as some threads do not have the
199bd670b35SErik Nordmark 	 * priviledge to open /dev/ip and the check in
200bd670b35SErik Nordmark 	 * secpolicy_spec_open() will fail the open
2010f1702c5SYu Xiangning 	 */
202bd670b35SErik Nordmark 	error = ldi_open_by_name((connp->conn_family == AF_INET6 ? DEV_IP6 :
203bd670b35SErik Nordmark 	    DEV_IP), IP_HELPER_STR, kcred, &helper->iphs_handle, li);
2040f1702c5SYu Xiangning 
2050f1702c5SYu Xiangning 	if (error != 0) {
206bd670b35SErik Nordmark 		kmem_free(helper, sizeof (ip_helper_stream_info_t));
2070f1702c5SYu Xiangning 		return (error);
2080f1702c5SYu Xiangning 	}
209bd670b35SErik Nordmark 	/* Make sure we are the only one */
210bd670b35SErik Nordmark 	mutex_enter(&connp->conn_lock);
211bd670b35SErik Nordmark 	if (connp->conn_helper_info != NULL) {
212bd670b35SErik Nordmark 		/* Some other thread won - discard this stream */
213bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
214bd670b35SErik Nordmark 		(void) ldi_close(helper->iphs_handle, 0, kcred);
215bd670b35SErik Nordmark 		kmem_free(helper, sizeof (ip_helper_stream_info_t));
216bd670b35SErik Nordmark 		return (0);
2170f1702c5SYu Xiangning 	}
218bd670b35SErik Nordmark 	connp->conn_helper_info = helper;
219bd670b35SErik Nordmark 	/*
220bd670b35SErik Nordmark 	 * Share connp with the helper stream. We hold conn_lock across this
221bd670b35SErik Nordmark 	 * operation.
222bd670b35SErik Nordmark 	 */
223bd670b35SErik Nordmark 	error = ldi_ioctl(helper->iphs_handle, SIOCSQPTR, (intptr_t)connp,
224bd670b35SErik Nordmark 	    FKIOCTL, kcred, &ret);
2250f1702c5SYu Xiangning 
226bd670b35SErik Nordmark 	if (error != 0) {
2270f1702c5SYu Xiangning 		/*
228bd670b35SErik Nordmark 		 * Passing in a zero flag indicates that an error
229bd670b35SErik Nordmark 		 * occured and stream was not shared
2300f1702c5SYu Xiangning 		 */
231bd670b35SErik Nordmark 		(void) ldi_close(helper->iphs_handle, 0, kcred);
232bd670b35SErik Nordmark 		kmem_free(helper, sizeof (ip_helper_stream_info_t));
233bd670b35SErik Nordmark 		connp->conn_helper_info = NULL;
2340f1702c5SYu Xiangning 	}
235bd670b35SErik Nordmark 	mutex_exit(&connp->conn_lock);
2360f1702c5SYu Xiangning 	return (error);
2370f1702c5SYu Xiangning }
2380f1702c5SYu Xiangning 
2390f1702c5SYu Xiangning /*
240bd670b35SErik Nordmark  * Public interface for freeing IP helper stream
241bd670b35SErik Nordmark  * Caller must ensure no concurrent use of the conn_t, which is normally
242bd670b35SErik Nordmark  * done by calling this from the close routine when the conn_t is quiesced.
2430f1702c5SYu Xiangning  */
244bd670b35SErik Nordmark void
ip_free_helper_stream(conn_t * connp)245bd670b35SErik Nordmark ip_free_helper_stream(conn_t *connp)
2460f1702c5SYu Xiangning {
247bd670b35SErik Nordmark 	ASSERT(!servicing_interrupt());
2480f1702c5SYu Xiangning 
249bd670b35SErik Nordmark 	if (connp->conn_helper_info == NULL)
250bd670b35SErik Nordmark 		return;
2510f1702c5SYu Xiangning 
252bd670b35SErik Nordmark 	ASSERT(connp->conn_helper_info->iphs_handle != NULL);
2530f1702c5SYu Xiangning 
254bd670b35SErik Nordmark 	connp->conn_helper_info->iphs_rq->q_ptr =
255bd670b35SErik Nordmark 	    connp->conn_helper_info->iphs_wq->q_ptr =
256bd670b35SErik Nordmark 	    connp->conn_helper_info->iphs_minfo;
257bd670b35SErik Nordmark 	(void) ldi_close(connp->conn_helper_info->iphs_handle,
258bd670b35SErik Nordmark 	    IP_HELPER_STR, kcred);
259bd670b35SErik Nordmark 	kmem_free(connp->conn_helper_info, sizeof (ip_helper_stream_info_t));
260bd670b35SErik Nordmark 	connp->conn_helper_info = NULL;
2610f1702c5SYu Xiangning }
262