xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_helper_stream.c (revision 11042:2d6e217af1b4)
18348SEric.Yu@Sun.COM /*
28348SEric.Yu@Sun.COM  * CDDL HEADER START
38348SEric.Yu@Sun.COM  *
48348SEric.Yu@Sun.COM  * The contents of this file are subject to the terms of the
58348SEric.Yu@Sun.COM  * Common Development and Distribution License (the "License").
68348SEric.Yu@Sun.COM  * You may not use this file except in compliance with the License.
78348SEric.Yu@Sun.COM  *
88348SEric.Yu@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98348SEric.Yu@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108348SEric.Yu@Sun.COM  * See the License for the specific language governing permissions
118348SEric.Yu@Sun.COM  * and limitations under the License.
128348SEric.Yu@Sun.COM  *
138348SEric.Yu@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148348SEric.Yu@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158348SEric.Yu@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168348SEric.Yu@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178348SEric.Yu@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188348SEric.Yu@Sun.COM  *
198348SEric.Yu@Sun.COM  * CDDL HEADER END
208348SEric.Yu@Sun.COM  */
218348SEric.Yu@Sun.COM 
228348SEric.Yu@Sun.COM /*
238477SRao.Shoaib@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
248348SEric.Yu@Sun.COM  * Use is subject to license terms.
258348SEric.Yu@Sun.COM  */
268348SEric.Yu@Sun.COM 
278348SEric.Yu@Sun.COM #include <sys/types.h>
288348SEric.Yu@Sun.COM #include <inet/ip.h>
298348SEric.Yu@Sun.COM #include <inet/ip_impl.h>
308348SEric.Yu@Sun.COM #include <inet/ipclassifier.h>
318348SEric.Yu@Sun.COM #include <inet/proto_set.h>
328348SEric.Yu@Sun.COM #include <sys/stream.h>
338348SEric.Yu@Sun.COM #include <sys/strsubr.h>
348348SEric.Yu@Sun.COM #include <sys/strsun.h>
358348SEric.Yu@Sun.COM #include <sys/cmn_err.h>
368348SEric.Yu@Sun.COM #include <sys/t_kuser.h>
378348SEric.Yu@Sun.COM #include <sys/tihdr.h>
388348SEric.Yu@Sun.COM #include <sys/pathname.h>
398348SEric.Yu@Sun.COM #include <sys/sockio.h>
408348SEric.Yu@Sun.COM #include <sys/vmem.h>
418348SEric.Yu@Sun.COM #include <sys/disp.h>
428348SEric.Yu@Sun.COM 
438348SEric.Yu@Sun.COM void ip_helper_wput(queue_t *q, mblk_t *mp);
448348SEric.Yu@Sun.COM 
458348SEric.Yu@Sun.COM static int ip_helper_stream_close(queue_t *, int);
468348SEric.Yu@Sun.COM 
478348SEric.Yu@Sun.COM static struct module_info ip_helper_stream_info =  {
488348SEric.Yu@Sun.COM 	0, "iphelper", IP_MOD_MINPSZ, IP_MOD_MAXPSZ, IP_MOD_HIWAT, IP_MOD_LOWAT
498348SEric.Yu@Sun.COM };
508348SEric.Yu@Sun.COM 
518348SEric.Yu@Sun.COM static struct qinit ip_helper_stream_rinit = {
528348SEric.Yu@Sun.COM 	NULL, NULL, NULL, ip_helper_stream_close, NULL,
538348SEric.Yu@Sun.COM 	&ip_helper_stream_info, NULL
548348SEric.Yu@Sun.COM };
558348SEric.Yu@Sun.COM 
568348SEric.Yu@Sun.COM static struct qinit ip_helper_stream_winit = {
578348SEric.Yu@Sun.COM 	(pfi_t)ip_helper_wput, (pfi_t)ip_wsrv, NULL, NULL, NULL,
588348SEric.Yu@Sun.COM 	&ip_helper_stream_info, NULL, NULL, NULL, STRUIOT_NONE
598348SEric.Yu@Sun.COM };
608348SEric.Yu@Sun.COM 
618348SEric.Yu@Sun.COM /*
628348SEric.Yu@Sun.COM  * set the q_ptr of the 'q' to the conn_t pointer passed in
638348SEric.Yu@Sun.COM  */
648348SEric.Yu@Sun.COM static void
ip_helper_share_conn(queue_t * q,mblk_t * mp,cred_t * crp)658477SRao.Shoaib@Sun.COM ip_helper_share_conn(queue_t *q, mblk_t *mp, cred_t *crp)
668348SEric.Yu@Sun.COM {
67*11042SErik.Nordmark@Sun.COM 	conn_t *connp = *((conn_t **)mp->b_cont->b_rptr);
68*11042SErik.Nordmark@Sun.COM 
698477SRao.Shoaib@Sun.COM 	/*
708477SRao.Shoaib@Sun.COM 	 * This operation is allowed only on helper streams with kcred
718477SRao.Shoaib@Sun.COM 	 */
728477SRao.Shoaib@Sun.COM 
738477SRao.Shoaib@Sun.COM 	if (kcred != crp || msgdsize(mp->b_cont) != sizeof (void *)) {
748477SRao.Shoaib@Sun.COM 		miocnak(q, mp, 0, EINVAL);
758477SRao.Shoaib@Sun.COM 		return;
768477SRao.Shoaib@Sun.COM 	}
778477SRao.Shoaib@Sun.COM 
78*11042SErik.Nordmark@Sun.COM 	connp->conn_helper_info->iphs_minfo = q->q_ptr;
79*11042SErik.Nordmark@Sun.COM 	connp->conn_helper_info->iphs_rq = RD(q);
80*11042SErik.Nordmark@Sun.COM 	connp->conn_helper_info->iphs_wq = WR(q);
81*11042SErik.Nordmark@Sun.COM 	WR(q)->q_ptr = RD(q)->q_ptr = (void *)connp;
82*11042SErik.Nordmark@Sun.COM 	connp->conn_rq = RD(q);
83*11042SErik.Nordmark@Sun.COM 	connp->conn_wq = WR(q);
848348SEric.Yu@Sun.COM 	miocack(q, mp, 0, 0);
858348SEric.Yu@Sun.COM }
868348SEric.Yu@Sun.COM 
878348SEric.Yu@Sun.COM void
ip_helper_wput(queue_t * q,mblk_t * mp)888348SEric.Yu@Sun.COM ip_helper_wput(queue_t *q, mblk_t *mp)
898348SEric.Yu@Sun.COM {
908348SEric.Yu@Sun.COM 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
918348SEric.Yu@Sun.COM 	if (DB_TYPE(mp) == M_IOCTL &&
928348SEric.Yu@Sun.COM 	    iocp->ioc_cmd == SIOCSQPTR) {
938477SRao.Shoaib@Sun.COM 		ip_helper_share_conn(q, mp, iocp->ioc_cr);
948348SEric.Yu@Sun.COM 	} else {
95*11042SErik.Nordmark@Sun.COM 		/* We only handle ioctl related messages here */
96*11042SErik.Nordmark@Sun.COM 		ASSERT(DB_TYPE(mp) != M_DATA);
97*11042SErik.Nordmark@Sun.COM 		ip_wput_nondata(q, mp);
988348SEric.Yu@Sun.COM 	}
998348SEric.Yu@Sun.COM }
1008348SEric.Yu@Sun.COM 
101*11042SErik.Nordmark@Sun.COM /* ARGSUSED3 */
1028348SEric.Yu@Sun.COM int
ip_helper_stream_setup(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp,boolean_t isv6)1038348SEric.Yu@Sun.COM ip_helper_stream_setup(queue_t *q, dev_t *devp, int flag, int sflag,
1048348SEric.Yu@Sun.COM     cred_t *credp, boolean_t isv6)
1058348SEric.Yu@Sun.COM {
1068348SEric.Yu@Sun.COM 	major_t			maj;
1078348SEric.Yu@Sun.COM 	ip_helper_minfo_t	*ip_minfop;
1088348SEric.Yu@Sun.COM 
1098348SEric.Yu@Sun.COM 	ASSERT((flag & ~(FKLYR)) == IP_HELPER_STR);
1108348SEric.Yu@Sun.COM 
1118348SEric.Yu@Sun.COM 	ASSERT(RD(q) == q);
1128348SEric.Yu@Sun.COM 
113*11042SErik.Nordmark@Sun.COM 	ip_minfop = kmem_alloc(sizeof (ip_helper_minfo_t), KM_SLEEP);
114*11042SErik.Nordmark@Sun.COM 	ASSERT(ip_minfop != NULL);
1158348SEric.Yu@Sun.COM 
1168348SEric.Yu@Sun.COM 	ip_minfop->ip_minfo_dev = 0;
1178348SEric.Yu@Sun.COM 	ip_minfop->ip_minfo_arena = NULL;
1188348SEric.Yu@Sun.COM 
1198348SEric.Yu@Sun.COM 	/*
1208348SEric.Yu@Sun.COM 	 * Clone the device, allocate minor device number
1218348SEric.Yu@Sun.COM 	 */
1228348SEric.Yu@Sun.COM 	if (ip_minor_arena_la != NULL)
1238348SEric.Yu@Sun.COM 		ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_la);
1248348SEric.Yu@Sun.COM 
1258348SEric.Yu@Sun.COM 	if (ip_minfop->ip_minfo_dev == 0) {
1268348SEric.Yu@Sun.COM 		/*
1278348SEric.Yu@Sun.COM 		 * numbers in the large arena are exhausted
1288348SEric.Yu@Sun.COM 		 * Try small arena.
1298348SEric.Yu@Sun.COM 		 * Or this is a 32 bit system, 32 bit systems do not have
1308348SEric.Yu@Sun.COM 		 * ip_minor_arena_la
1318348SEric.Yu@Sun.COM 		 */
1328348SEric.Yu@Sun.COM 		ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_sa);
1338348SEric.Yu@Sun.COM 		if (ip_minfop->ip_minfo_dev == 0) {
1348348SEric.Yu@Sun.COM 			return (EBUSY);
1358348SEric.Yu@Sun.COM 		}
1368348SEric.Yu@Sun.COM 		ip_minfop->ip_minfo_arena = ip_minor_arena_sa;
1378348SEric.Yu@Sun.COM 	} else {
1388348SEric.Yu@Sun.COM 		ip_minfop->ip_minfo_arena = ip_minor_arena_la;
1398348SEric.Yu@Sun.COM 	}
1408348SEric.Yu@Sun.COM 
1418348SEric.Yu@Sun.COM 
1428348SEric.Yu@Sun.COM 	ASSERT(ip_minfop->ip_minfo_dev != 0);
1438348SEric.Yu@Sun.COM 	ASSERT(ip_minfop->ip_minfo_arena != NULL);
1448348SEric.Yu@Sun.COM 
1458348SEric.Yu@Sun.COM 	RD(q)->q_ptr = WR(q)->q_ptr = ip_minfop;
1468348SEric.Yu@Sun.COM 
1478348SEric.Yu@Sun.COM 	maj = getemajor(*devp);
1488348SEric.Yu@Sun.COM 	*devp = makedevice(maj, (ulong_t)(ip_minfop->ip_minfo_dev));
1498348SEric.Yu@Sun.COM 
1508348SEric.Yu@Sun.COM 	q->q_qinfo = &ip_helper_stream_rinit;
1518348SEric.Yu@Sun.COM 	WR(q)->q_qinfo = &ip_helper_stream_winit;
1528348SEric.Yu@Sun.COM 	qprocson(q);
1538348SEric.Yu@Sun.COM 	return (0);
1548348SEric.Yu@Sun.COM }
1558348SEric.Yu@Sun.COM 
156*11042SErik.Nordmark@Sun.COM /* ARGSUSED1 */
1578348SEric.Yu@Sun.COM static int
ip_helper_stream_close(queue_t * q,int flag)1588348SEric.Yu@Sun.COM ip_helper_stream_close(queue_t *q, int flag)
1598348SEric.Yu@Sun.COM {
1608348SEric.Yu@Sun.COM 	ip_helper_minfo_t *ip_minfop;
1618348SEric.Yu@Sun.COM 
1628348SEric.Yu@Sun.COM 	qprocsoff(q);
1638348SEric.Yu@Sun.COM 	ip_minfop = (q)->q_ptr;
1648348SEric.Yu@Sun.COM 	inet_minor_free(ip_minfop->ip_minfo_arena,
1658348SEric.Yu@Sun.COM 	    ip_minfop->ip_minfo_dev);
1668348SEric.Yu@Sun.COM 	kmem_free(ip_minfop, sizeof (ip_helper_minfo_t));
1678348SEric.Yu@Sun.COM 	RD(q)->q_ptr = NULL;
1688348SEric.Yu@Sun.COM 	WR(q)->q_ptr = NULL;
1698348SEric.Yu@Sun.COM 	return (0);
1708348SEric.Yu@Sun.COM }
1718348SEric.Yu@Sun.COM 
1728348SEric.Yu@Sun.COM /*
1738348SEric.Yu@Sun.COM  * Public interface for creating an IP stream with shared conn_t
174*11042SErik.Nordmark@Sun.COM  * Handles multiple callers in parallel by using conn_lock.
175*11042SErik.Nordmark@Sun.COM  * Note that we allocate the helper stream without any locks, which means
176*11042SErik.Nordmark@Sun.COM  * we might need to free it if we had two threads doing this concurrently
177*11042SErik.Nordmark@Sun.COM  * for the conn_t.
1788348SEric.Yu@Sun.COM  */
1798348SEric.Yu@Sun.COM int
ip_create_helper_stream(conn_t * connp,ldi_ident_t li)1808348SEric.Yu@Sun.COM ip_create_helper_stream(conn_t *connp, ldi_ident_t li)
1818348SEric.Yu@Sun.COM {
182*11042SErik.Nordmark@Sun.COM 	ip_helper_stream_info_t *helper;
1838348SEric.Yu@Sun.COM 	int	error;
1848348SEric.Yu@Sun.COM 	int	ret;
1858348SEric.Yu@Sun.COM 
1868348SEric.Yu@Sun.COM 	ASSERT(!servicing_interrupt());
1878348SEric.Yu@Sun.COM 
188*11042SErik.Nordmark@Sun.COM 	if (connp->conn_helper_info != NULL) {
189*11042SErik.Nordmark@Sun.COM 		/* Already allocated */
190*11042SErik.Nordmark@Sun.COM 		return (0);
191*11042SErik.Nordmark@Sun.COM 	}
192*11042SErik.Nordmark@Sun.COM 
1938348SEric.Yu@Sun.COM 	error = 0;
194*11042SErik.Nordmark@Sun.COM 	helper = kmem_alloc(sizeof (ip_helper_stream_info_t), KM_SLEEP);
195*11042SErik.Nordmark@Sun.COM 
196*11042SErik.Nordmark@Sun.COM 	/*
197*11042SErik.Nordmark@Sun.COM 	 * open ip device via the layered interface.
198*11042SErik.Nordmark@Sun.COM 	 * pass in kcred as some threads do not have the
199*11042SErik.Nordmark@Sun.COM 	 * priviledge to open /dev/ip and the check in
200*11042SErik.Nordmark@Sun.COM 	 * secpolicy_spec_open() will fail the open
201*11042SErik.Nordmark@Sun.COM 	 */
202*11042SErik.Nordmark@Sun.COM 	error = ldi_open_by_name((connp->conn_family == AF_INET6 ? DEV_IP6 :
203*11042SErik.Nordmark@Sun.COM 	    DEV_IP), IP_HELPER_STR, kcred, &helper->iphs_handle, li);
204*11042SErik.Nordmark@Sun.COM 
205*11042SErik.Nordmark@Sun.COM 	if (error != 0) {
206*11042SErik.Nordmark@Sun.COM 		kmem_free(helper, sizeof (ip_helper_stream_info_t));
207*11042SErik.Nordmark@Sun.COM 		return (error);
208*11042SErik.Nordmark@Sun.COM 	}
209*11042SErik.Nordmark@Sun.COM 	/* Make sure we are the only one */
210*11042SErik.Nordmark@Sun.COM 	mutex_enter(&connp->conn_lock);
211*11042SErik.Nordmark@Sun.COM 	if (connp->conn_helper_info != NULL) {
212*11042SErik.Nordmark@Sun.COM 		/* Some other thread won - discard this stream */
213*11042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
214*11042SErik.Nordmark@Sun.COM 		(void) ldi_close(helper->iphs_handle, 0, kcred);
215*11042SErik.Nordmark@Sun.COM 		kmem_free(helper, sizeof (ip_helper_stream_info_t));
216*11042SErik.Nordmark@Sun.COM 		return (0);
217*11042SErik.Nordmark@Sun.COM 	}
218*11042SErik.Nordmark@Sun.COM 	connp->conn_helper_info = helper;
219*11042SErik.Nordmark@Sun.COM 	/*
220*11042SErik.Nordmark@Sun.COM 	 * Share connp with the helper stream. We hold conn_lock across this
221*11042SErik.Nordmark@Sun.COM 	 * operation.
222*11042SErik.Nordmark@Sun.COM 	 */
223*11042SErik.Nordmark@Sun.COM 	error = ldi_ioctl(helper->iphs_handle, SIOCSQPTR, (intptr_t)connp,
224*11042SErik.Nordmark@Sun.COM 	    FKIOCTL, kcred, &ret);
225*11042SErik.Nordmark@Sun.COM 
226*11042SErik.Nordmark@Sun.COM 	if (error != 0) {
2278449SMike.Cheng@Sun.COM 		/*
228*11042SErik.Nordmark@Sun.COM 		 * Passing in a zero flag indicates that an error
229*11042SErik.Nordmark@Sun.COM 		 * occured and stream was not shared
2308348SEric.Yu@Sun.COM 		 */
231*11042SErik.Nordmark@Sun.COM 		(void) ldi_close(helper->iphs_handle, 0, kcred);
232*11042SErik.Nordmark@Sun.COM 		kmem_free(helper, sizeof (ip_helper_stream_info_t));
233*11042SErik.Nordmark@Sun.COM 		connp->conn_helper_info = NULL;
2348348SEric.Yu@Sun.COM 	}
235*11042SErik.Nordmark@Sun.COM 	mutex_exit(&connp->conn_lock);
2368348SEric.Yu@Sun.COM 	return (error);
2378348SEric.Yu@Sun.COM }
2388348SEric.Yu@Sun.COM 
2398348SEric.Yu@Sun.COM /*
2408477SRao.Shoaib@Sun.COM  * Public interface for freeing IP helper stream
241*11042SErik.Nordmark@Sun.COM  * Caller must ensure no concurrent use of the conn_t, which is normally
242*11042SErik.Nordmark@Sun.COM  * done by calling this from the close routine when the conn_t is quiesced.
2438348SEric.Yu@Sun.COM  */
2448348SEric.Yu@Sun.COM void
ip_free_helper_stream(conn_t * connp)2458477SRao.Shoaib@Sun.COM ip_free_helper_stream(conn_t *connp)
2468348SEric.Yu@Sun.COM {
2478348SEric.Yu@Sun.COM 	ASSERT(!servicing_interrupt());
2488348SEric.Yu@Sun.COM 
249*11042SErik.Nordmark@Sun.COM 	if (connp->conn_helper_info == NULL)
250*11042SErik.Nordmark@Sun.COM 		return;
2518348SEric.Yu@Sun.COM 
252*11042SErik.Nordmark@Sun.COM 	ASSERT(connp->conn_helper_info->iphs_handle != NULL);
2538348SEric.Yu@Sun.COM 
254*11042SErik.Nordmark@Sun.COM 	connp->conn_helper_info->iphs_rq->q_ptr =
255*11042SErik.Nordmark@Sun.COM 	    connp->conn_helper_info->iphs_wq->q_ptr =
256*11042SErik.Nordmark@Sun.COM 	    connp->conn_helper_info->iphs_minfo;
257*11042SErik.Nordmark@Sun.COM 	(void) ldi_close(connp->conn_helper_info->iphs_handle,
258*11042SErik.Nordmark@Sun.COM 	    IP_HELPER_STR, kcred);
259*11042SErik.Nordmark@Sun.COM 	kmem_free(connp->conn_helper_info, sizeof (ip_helper_stream_info_t));
2608348SEric.Yu@Sun.COM 	connp->conn_helper_info = NULL;
2618348SEric.Yu@Sun.COM }
262