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