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 * Or this is a 32 bit system, 32 bit systems do not have 131 * ip_minor_arena_la 132 */ 133 ip_minfop->ip_minfo_dev = inet_minor_alloc(ip_minor_arena_sa); 134 if (ip_minfop->ip_minfo_dev == 0) { 135 return (EBUSY); 136 } 137 ip_minfop->ip_minfo_arena = ip_minor_arena_sa; 138 } else { 139 ip_minfop->ip_minfo_arena = ip_minor_arena_la; 140 } 141 142 143 ASSERT(ip_minfop->ip_minfo_dev != 0); 144 ASSERT(ip_minfop->ip_minfo_arena != NULL); 145 146 RD(q)->q_ptr = WR(q)->q_ptr = ip_minfop; 147 148 maj = getemajor(*devp); 149 *devp = makedevice(maj, (ulong_t)(ip_minfop->ip_minfo_dev)); 150 151 q->q_qinfo = &ip_helper_stream_rinit; 152 WR(q)->q_qinfo = &ip_helper_stream_winit; 153 qprocson(q); 154 return (0); 155 } 156 157 /* ARGSUSED */ 158 static int 159 ip_helper_stream_close(queue_t *q, int flag __unused, cred_t *credp __unused) 160 { 161 ip_helper_minfo_t *ip_minfop; 162 163 qprocsoff(q); 164 ip_minfop = (q)->q_ptr; 165 inet_minor_free(ip_minfop->ip_minfo_arena, 166 ip_minfop->ip_minfo_dev); 167 kmem_free(ip_minfop, sizeof (ip_helper_minfo_t)); 168 RD(q)->q_ptr = NULL; 169 WR(q)->q_ptr = NULL; 170 return (0); 171 } 172 173 /* 174 * Public interface for creating an IP stream with shared conn_t 175 * Handles multiple callers in parallel by using conn_lock. 176 * Note that we allocate the helper stream without any locks, which means 177 * we might need to free it if we had two threads doing this concurrently 178 * for the conn_t. 179 */ 180 int 181 ip_create_helper_stream(conn_t *connp, ldi_ident_t li) 182 { 183 ip_helper_stream_info_t *helper; 184 int error; 185 int ret; 186 187 ASSERT(!servicing_interrupt()); 188 189 if (connp->conn_helper_info != NULL) { 190 /* Already allocated */ 191 return (0); 192 } 193 194 error = 0; 195 helper = kmem_alloc(sizeof (ip_helper_stream_info_t), KM_SLEEP); 196 197 /* 198 * open ip device via the layered interface. 199 * pass in kcred as some threads do not have the 200 * priviledge to open /dev/ip and the check in 201 * secpolicy_spec_open() will fail the open 202 */ 203 error = ldi_open_by_name((connp->conn_family == AF_INET6 ? DEV_IP6 : 204 DEV_IP), IP_HELPER_STR, kcred, &helper->iphs_handle, li); 205 206 if (error != 0) { 207 kmem_free(helper, sizeof (ip_helper_stream_info_t)); 208 return (error); 209 } 210 /* Make sure we are the only one */ 211 mutex_enter(&connp->conn_lock); 212 if (connp->conn_helper_info != NULL) { 213 /* Some other thread won - discard this stream */ 214 mutex_exit(&connp->conn_lock); 215 (void) ldi_close(helper->iphs_handle, 0, kcred); 216 kmem_free(helper, sizeof (ip_helper_stream_info_t)); 217 return (0); 218 } 219 connp->conn_helper_info = helper; 220 /* 221 * Share connp with the helper stream. We hold conn_lock across this 222 * operation. 223 */ 224 error = ldi_ioctl(helper->iphs_handle, SIOCSQPTR, (intptr_t)connp, 225 FKIOCTL, kcred, &ret); 226 227 if (error != 0) { 228 /* 229 * Passing in a zero flag indicates that an error 230 * occured and stream was not shared 231 */ 232 (void) ldi_close(helper->iphs_handle, 0, kcred); 233 kmem_free(helper, sizeof (ip_helper_stream_info_t)); 234 connp->conn_helper_info = NULL; 235 } 236 mutex_exit(&connp->conn_lock); 237 return (error); 238 } 239 240 /* 241 * Public interface for freeing IP helper stream 242 * Caller must ensure no concurrent use of the conn_t, which is normally 243 * done by calling this from the close routine when the conn_t is quiesced. 244 */ 245 void 246 ip_free_helper_stream(conn_t *connp) 247 { 248 ASSERT(!servicing_interrupt()); 249 250 if (connp->conn_helper_info == NULL) 251 return; 252 253 ASSERT(connp->conn_helper_info->iphs_handle != NULL); 254 255 connp->conn_helper_info->iphs_rq->q_ptr = 256 connp->conn_helper_info->iphs_wq->q_ptr = 257 connp->conn_helper_info->iphs_minfo; 258 (void) ldi_close(connp->conn_helper_info->iphs_handle, 259 IP_HELPER_STR, kcred); 260 kmem_free(connp->conn_helper_info, sizeof (ip_helper_stream_info_t)); 261 connp->conn_helper_info = NULL; 262 } 263