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