1 8348 Eric /* 2 8348 Eric * CDDL HEADER START 3 8348 Eric * 4 8348 Eric * The contents of this file are subject to the terms of the 5 8348 Eric * Common Development and Distribution License (the "License"). 6 8348 Eric * You may not use this file except in compliance with the License. 7 8348 Eric * 8 8348 Eric * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 8348 Eric * or http://www.opensolaris.org/os/licensing. 10 8348 Eric * See the License for the specific language governing permissions 11 8348 Eric * and limitations under the License. 12 8348 Eric * 13 8348 Eric * When distributing Covered Code, include this CDDL HEADER in each 14 8348 Eric * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 8348 Eric * If applicable, add the following below this CDDL HEADER, with the 16 8348 Eric * fields enclosed by brackets "[]" replaced with your own identifying 17 8348 Eric * information: Portions Copyright [yyyy] [name of copyright owner] 18 8348 Eric * 19 8348 Eric * CDDL HEADER END 20 8348 Eric */ 21 8348 Eric 22 8348 Eric /* 23 8477 Rao * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 8348 Eric * Use is subject to license terms. 25 8348 Eric */ 26 8348 Eric 27 8348 Eric #include <sys/types.h> 28 8348 Eric #include <inet/ip.h> 29 8348 Eric #include <inet/ip_impl.h> 30 8348 Eric #include <inet/ipclassifier.h> 31 8348 Eric #include <inet/proto_set.h> 32 8348 Eric #include <sys/stream.h> 33 8348 Eric #include <sys/strsubr.h> 34 8348 Eric #include <sys/strsun.h> 35 8348 Eric #include <sys/cmn_err.h> 36 8348 Eric #include <sys/t_kuser.h> 37 8348 Eric #include <sys/tihdr.h> 38 8348 Eric #include <sys/pathname.h> 39 8348 Eric #include <sys/sockio.h> 40 8348 Eric #include <sys/vmem.h> 41 8348 Eric #include <sys/disp.h> 42 8348 Eric 43 8348 Eric void ip_helper_wput(queue_t *q, mblk_t *mp); 44 8348 Eric 45 8348 Eric static int ip_helper_stream_close(queue_t *, int); 46 8348 Eric 47 8348 Eric static struct module_info ip_helper_stream_info = { 48 8348 Eric 0, "iphelper", IP_MOD_MINPSZ, IP_MOD_MAXPSZ, IP_MOD_HIWAT, IP_MOD_LOWAT 49 8348 Eric }; 50 8348 Eric 51 8348 Eric static struct qinit ip_helper_stream_rinit = { 52 8348 Eric NULL, NULL, NULL, ip_helper_stream_close, NULL, 53 8348 Eric &ip_helper_stream_info, NULL 54 8348 Eric }; 55 8348 Eric 56 8348 Eric static struct qinit ip_helper_stream_winit = { 57 8348 Eric (pfi_t)ip_helper_wput, (pfi_t)ip_wsrv, NULL, NULL, NULL, 58 8348 Eric &ip_helper_stream_info, NULL, NULL, NULL, STRUIOT_NONE 59 8348 Eric }; 60 8348 Eric 61 8348 Eric /* 62 8348 Eric * set the q_ptr of the 'q' to the conn_t pointer passed in 63 8348 Eric */ 64 8348 Eric static void 65 8477 Rao ip_helper_share_conn(queue_t *q, mblk_t *mp, cred_t *crp) 66 8348 Eric { 67 11042 Erik conn_t *connp = *((conn_t **)mp->b_cont->b_rptr); 68 11042 Erik 69 8477 Rao /* 70 8477 Rao * This operation is allowed only on helper streams with kcred 71 8477 Rao */ 72 8477 Rao 73 8477 Rao if (kcred != crp || msgdsize(mp->b_cont) != sizeof (void *)) { 74 8477 Rao miocnak(q, mp, 0, EINVAL); 75 8477 Rao return; 76 8477 Rao } 77 8477 Rao 78 11042 Erik connp->conn_helper_info->iphs_minfo = q->q_ptr; 79 11042 Erik connp->conn_helper_info->iphs_rq = RD(q); 80 11042 Erik connp->conn_helper_info->iphs_wq = WR(q); 81 11042 Erik WR(q)->q_ptr = RD(q)->q_ptr = (void *)connp; 82 11042 Erik connp->conn_rq = RD(q); 83 11042 Erik connp->conn_wq = WR(q); 84 8348 Eric miocack(q, mp, 0, 0); 85 8348 Eric } 86 8348 Eric 87 8348 Eric void 88 8348 Eric ip_helper_wput(queue_t *q, mblk_t *mp) 89 8348 Eric { 90 8348 Eric struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 91 8348 Eric if (DB_TYPE(mp) == M_IOCTL && 92 8348 Eric iocp->ioc_cmd == SIOCSQPTR) { 93 8477 Rao ip_helper_share_conn(q, mp, iocp->ioc_cr); 94 8348 Eric } else { 95 11042 Erik /* We only handle ioctl related messages here */ 96 11042 Erik ASSERT(DB_TYPE(mp) != M_DATA); 97 11042 Erik ip_wput_nondata(q, mp); 98 8348 Eric } 99 8348 Eric } 100 8348 Eric 101 11042 Erik /* ARGSUSED3 */ 102 8348 Eric int 103 8348 Eric ip_helper_stream_setup(queue_t *q, dev_t *devp, int flag, int sflag, 104 8348 Eric cred_t *credp, boolean_t isv6) 105 8348 Eric { 106 8348 Eric major_t maj; 107 8348 Eric ip_helper_minfo_t *ip_minfop; 108 8348 Eric 109 8348 Eric ASSERT((flag & ~(FKLYR)) == IP_HELPER_STR); 110 8348 Eric 111 8348 Eric ASSERT(RD(q) == q); 112 8348 Eric 113 11042 Erik ip_minfop = kmem_alloc(sizeof (ip_helper_minfo_t), KM_SLEEP); 114 11042 Erik ASSERT(ip_minfop != NULL); 115 8348 Eric 116 8348 Eric ip_minfop->ip_minfo_dev = 0; 117 8348 Eric ip_minfop->ip_minfo_arena = NULL; 118 8348 Eric 119 8348 Eric /* 120 8348 Eric * Clone the device, allocate minor device number 121 8348 Eric */ 122 8348 Eric if (ip_minor_arena_la != NULL) 123 8348 Eric ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_la); 124 8348 Eric 125 8348 Eric if (ip_minfop->ip_minfo_dev == 0) { 126 8348 Eric /* 127 8348 Eric * numbers in the large arena are exhausted 128 8348 Eric * Try small arena. 129 8348 Eric * Or this is a 32 bit system, 32 bit systems do not have 130 8348 Eric * ip_minor_arena_la 131 8348 Eric */ 132 8348 Eric ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_sa); 133 8348 Eric if (ip_minfop->ip_minfo_dev == 0) { 134 8348 Eric return (EBUSY); 135 8348 Eric } 136 8348 Eric ip_minfop->ip_minfo_arena = ip_minor_arena_sa; 137 8348 Eric } else { 138 8348 Eric ip_minfop->ip_minfo_arena = ip_minor_arena_la; 139 8348 Eric } 140 8348 Eric 141 8348 Eric 142 8348 Eric ASSERT(ip_minfop->ip_minfo_dev != 0); 143 8348 Eric ASSERT(ip_minfop->ip_minfo_arena != NULL); 144 8348 Eric 145 8348 Eric RD(q)->q_ptr = WR(q)->q_ptr = ip_minfop; 146 8348 Eric 147 8348 Eric maj = getemajor(*devp); 148 8348 Eric *devp = makedevice(maj, (ulong_t)(ip_minfop->ip_minfo_dev)); 149 8348 Eric 150 8348 Eric q->q_qinfo = &ip_helper_stream_rinit; 151 8348 Eric WR(q)->q_qinfo = &ip_helper_stream_winit; 152 8348 Eric qprocson(q); 153 8348 Eric return (0); 154 8348 Eric } 155 8348 Eric 156 11042 Erik /* ARGSUSED1 */ 157 8348 Eric static int 158 8348 Eric ip_helper_stream_close(queue_t *q, int flag) 159 8348 Eric { 160 8348 Eric ip_helper_minfo_t *ip_minfop; 161 8348 Eric 162 8348 Eric qprocsoff(q); 163 8348 Eric ip_minfop = (q)->q_ptr; 164 8348 Eric inet_minor_free(ip_minfop->ip_minfo_arena, 165 8348 Eric ip_minfop->ip_minfo_dev); 166 8348 Eric kmem_free(ip_minfop, sizeof (ip_helper_minfo_t)); 167 8348 Eric RD(q)->q_ptr = NULL; 168 8348 Eric WR(q)->q_ptr = NULL; 169 8348 Eric return (0); 170 8348 Eric } 171 8348 Eric 172 8348 Eric /* 173 8348 Eric * Public interface for creating an IP stream with shared conn_t 174 11042 Erik * Handles multiple callers in parallel by using conn_lock. 175 11042 Erik * Note that we allocate the helper stream without any locks, which means 176 11042 Erik * we might need to free it if we had two threads doing this concurrently 177 11042 Erik * for the conn_t. 178 8348 Eric */ 179 8348 Eric int 180 8348 Eric ip_create_helper_stream(conn_t *connp, ldi_ident_t li) 181 8348 Eric { 182 11042 Erik ip_helper_stream_info_t *helper; 183 8348 Eric int error; 184 8348 Eric int ret; 185 8348 Eric 186 8348 Eric ASSERT(!servicing_interrupt()); 187 8348 Eric 188 11042 Erik if (connp->conn_helper_info != NULL) { 189 11042 Erik /* Already allocated */ 190 11042 Erik return (0); 191 11042 Erik } 192 11042 Erik 193 8348 Eric error = 0; 194 11042 Erik helper = kmem_alloc(sizeof (ip_helper_stream_info_t), KM_SLEEP); 195 11042 Erik 196 11042 Erik /* 197 11042 Erik * open ip device via the layered interface. 198 11042 Erik * pass in kcred as some threads do not have the 199 11042 Erik * priviledge to open /dev/ip and the check in 200 11042 Erik * secpolicy_spec_open() will fail the open 201 11042 Erik */ 202 11042 Erik error = ldi_open_by_name((connp->conn_family == AF_INET6 ? DEV_IP6 : 203 11042 Erik DEV_IP), IP_HELPER_STR, kcred, &helper->iphs_handle, li); 204 11042 Erik 205 11042 Erik if (error != 0) { 206 11042 Erik kmem_free(helper, sizeof (ip_helper_stream_info_t)); 207 11042 Erik return (error); 208 11042 Erik } 209 11042 Erik /* Make sure we are the only one */ 210 11042 Erik mutex_enter(&connp->conn_lock); 211 11042 Erik if (connp->conn_helper_info != NULL) { 212 11042 Erik /* Some other thread won - discard this stream */ 213 11042 Erik mutex_exit(&connp->conn_lock); 214 11042 Erik (void) ldi_close(helper->iphs_handle, 0, kcred); 215 11042 Erik kmem_free(helper, sizeof (ip_helper_stream_info_t)); 216 11042 Erik return (0); 217 11042 Erik } 218 11042 Erik connp->conn_helper_info = helper; 219 11042 Erik /* 220 11042 Erik * Share connp with the helper stream. We hold conn_lock across this 221 11042 Erik * operation. 222 11042 Erik */ 223 11042 Erik error = ldi_ioctl(helper->iphs_handle, SIOCSQPTR, (intptr_t)connp, 224 11042 Erik FKIOCTL, kcred, &ret); 225 11042 Erik 226 11042 Erik if (error != 0) { 227 8449 Mike /* 228 11042 Erik * Passing in a zero flag indicates that an error 229 11042 Erik * occured and stream was not shared 230 8449 Mike */ 231 11042 Erik (void) ldi_close(helper->iphs_handle, 0, kcred); 232 11042 Erik kmem_free(helper, sizeof (ip_helper_stream_info_t)); 233 11042 Erik connp->conn_helper_info = NULL; 234 8348 Eric } 235 11042 Erik mutex_exit(&connp->conn_lock); 236 8348 Eric return (error); 237 8348 Eric } 238 8348 Eric 239 8348 Eric /* 240 8477 Rao * Public interface for freeing IP helper stream 241 11042 Erik * Caller must ensure no concurrent use of the conn_t, which is normally 242 11042 Erik * done by calling this from the close routine when the conn_t is quiesced. 243 8348 Eric */ 244 8348 Eric void 245 8477 Rao ip_free_helper_stream(conn_t *connp) 246 8348 Eric { 247 8348 Eric ASSERT(!servicing_interrupt()); 248 8444 Rao 249 11042 Erik if (connp->conn_helper_info == NULL) 250 11042 Erik return; 251 8348 Eric 252 11042 Erik ASSERT(connp->conn_helper_info->iphs_handle != NULL); 253 8348 Eric 254 11042 Erik connp->conn_helper_info->iphs_rq->q_ptr = 255 11042 Erik connp->conn_helper_info->iphs_wq->q_ptr = 256 11042 Erik connp->conn_helper_info->iphs_minfo; 257 11042 Erik (void) ldi_close(connp->conn_helper_info->iphs_handle, 258 11042 Erik IP_HELPER_STR, kcred); 259 11042 Erik kmem_free(connp->conn_helper_info, sizeof (ip_helper_stream_info_t)); 260 8348 Eric connp->conn_helper_info = NULL; 261 8348 Eric } 262