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 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/sockio.h>
27 #include <sys/stream.h>
28 #include <sys/errno.h>
29 #include <sys/cmn_err.h>
30 #include <sys/strsun.h>
31 #include <inet/common.h>
32 #include <net/if.h>
33 #include <net/if_types.h>
34 #include <inet/mi.h>
35 #include <sys/t_kuser.h>
36 #include <sys/stropts.h>
37 #include <sys/pathname.h>
38 #include <sys/kstr.h>
39 #include <sys/timod.h>
40 #include <sys/sunddi.h>
41 #include <sys/ib/clients/rds/rds.h>
42 #include <sys/ib/clients/rds/rds_transport.h>
43
44 /*
45 * Just pass the ioctl to IP and the result to the caller.
46 */
47 int
rds_do_ip_ioctl(int cmd,int len,void * arg)48 rds_do_ip_ioctl(int cmd, int len, void *arg)
49 {
50 vnode_t *kkvp, *vp;
51 TIUSER *tiptr;
52 struct strioctl iocb;
53 k_sigset_t smask;
54 int err = 0;
55
56 if (lookupname("/dev/udp", UIO_SYSSPACE, FOLLOW, NULLVPP, &kkvp) == 0) {
57 if (t_kopen((file_t *)NULL, kkvp->v_rdev, FREAD|FWRITE,
58 &tiptr, CRED()) == 0) {
59 vp = tiptr->fp->f_vnode;
60 } else {
61 VN_RELE(kkvp);
62 return (EPROTO);
63 }
64 } else {
65 return (EPROTO);
66 }
67
68 iocb.ic_cmd = cmd;
69 iocb.ic_timout = 0;
70 iocb.ic_len = len;
71 iocb.ic_dp = (caddr_t)arg;
72 sigintr(&smask, 0);
73 err = kstr_ioctl(vp, I_STR, (intptr_t)&iocb);
74 sigunintr(&smask);
75 (void) t_kclose(tiptr, 0);
76 VN_RELE(kkvp);
77 return (err);
78 }
79
80 /*
81 * Check if the IP interface named by `lifrp' is RDS-capable.
82 */
83 static boolean_t
rds_capable_interface(struct lifreq * lifrp)84 rds_capable_interface(struct lifreq *lifrp)
85 {
86 char ifname[LIFNAMSIZ];
87 char drv[MAXLINKNAMELEN];
88 uint_t ppa;
89 char *cp;
90
91 if (lifrp->lifr_type == IFT_IB)
92 return (B_TRUE);
93
94 /*
95 * Strip off the logical interface portion before getting
96 * intimate with the name.
97 */
98 (void) strlcpy(ifname, lifrp->lifr_name, LIFNAMSIZ);
99 if ((cp = strchr(ifname, ':')) != NULL)
100 *cp = '\0';
101
102 if (strcmp("lo0", ifname) == 0) {
103 /*
104 * loopback is considered RDS-capable
105 */
106 return (B_TRUE);
107 }
108
109 return (
110 ddi_parse_dlen(ifname, drv, MAXLINKNAMELEN, &ppa) == DDI_SUCCESS &&
111 rds_transport_ops->rds_transport_if_lookup_by_name(drv));
112 }
113
114 /*
115 * Issue an SIOCGLIFCONF down to IP and return the result in `lifcp'.
116 * lifcp->lifc_buf is dynamically allocated to be *bufsizep bytes.
117 */
118 static int
rds_do_lifconf(struct lifconf * lifcp,uint_t * bufsizep)119 rds_do_lifconf(struct lifconf *lifcp, uint_t *bufsizep)
120 {
121 int err;
122 int nifs;
123
124 if ((err = rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), &nifs)) != 0)
125 return (err);
126
127 /*
128 * Pad the interface count to account for additional interfaces that
129 * may have been configured between the SIOCGLIFNUM and SIOCGLIFCONF.
130 */
131 nifs += 4;
132
133 bzero(lifcp, sizeof (struct lifconf));
134 lifcp->lifc_family = AF_INET;
135 lifcp->lifc_len = *bufsizep = (nifs * sizeof (struct lifreq));
136 lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_NOSLEEP);
137 if (lifcp->lifc_buf == NULL)
138 return (ENOMEM);
139
140 err = rds_do_ip_ioctl(SIOCGLIFCONF, sizeof (struct lifconf), lifcp);
141 if (err != 0) {
142 kmem_free(lifcp->lifc_buf, *bufsizep);
143 return (err);
144 }
145 return (0);
146 }
147
148 void
rds_ioctl_copyin_done(queue_t * q,mblk_t * mp)149 rds_ioctl_copyin_done(queue_t *q, mblk_t *mp)
150 {
151 void *addr;
152 mblk_t *mp1;
153 int err = 0;
154 struct iocblk *iocp = (void *)mp->b_rptr;
155
156 if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) {
157 err = EPROTO;
158 goto done;
159 }
160
161 addr = mp1->b_rptr;
162
163 switch (iocp->ioc_cmd) {
164 case SIOCGIFNUM: {
165 uint_t bufsize;
166 struct lifconf lifc;
167 struct lifreq *lifrp;
168 int i, nifs, retval = 0;
169
170 if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0)
171 break;
172
173 nifs = lifc.lifc_len / sizeof (struct lifreq);
174 for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) {
175 if (strlen(lifrp->lifr_name) <= IFNAMSIZ &&
176 rds_capable_interface(lifrp)) {
177 retval++;
178 }
179 }
180 *((int *)addr) = retval;
181 kmem_free(lifc.lifc_buf, bufsize);
182 break;
183 }
184
185 case O_SIOCGIFCONF:
186 case SIOCGIFCONF: {
187 STRUCT_HANDLE(ifconf, ifc);
188 caddr_t ubuf_addr;
189 int ubuf_size;
190 uint_t bufsize;
191 int i, nifs;
192 struct lifconf lifc;
193 struct lifreq *lifrp;
194 struct ifreq *ifrp;
195
196 STRUCT_SET_HANDLE(ifc, iocp->ioc_flag, (struct ifconf *)addr);
197 ubuf_size = STRUCT_FGET(ifc, ifc_len);
198 ubuf_addr = STRUCT_FGETP(ifc, ifc_buf);
199
200 if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0)
201 break;
202
203 mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE);
204 if (mp1 == NULL) {
205 err = ENOMEM;
206 kmem_free(lifc.lifc_buf, bufsize);
207 break;
208 }
209
210 ifrp = (void *)mp1->b_rptr;
211 nifs = lifc.lifc_len / sizeof (struct lifreq);
212 for (lifrp = lifc.lifc_req, i = 0; i < nifs &&
213 MBLKTAIL(mp1) >= sizeof (struct ifreq); i++, lifrp++) {
214 /*
215 * Skip entries that are impossible to return with
216 * SIOCGIFCONF, or not RDS-capable.
217 */
218 if (strlen(lifrp->lifr_name) > IFNAMSIZ ||
219 !rds_capable_interface(lifrp)) {
220 continue;
221 }
222
223 ifrp->ifr_addr = *(struct sockaddr *)&lifrp->lifr_addr;
224 ifrp->ifr_addr.sa_family = AF_INET_OFFLOAD;
225 (void) strlcpy(ifrp->ifr_name, lifrp->lifr_name,
226 IFNAMSIZ);
227 ifrp++;
228 mp1->b_wptr += sizeof (struct ifreq);
229 }
230
231 STRUCT_FSET(ifc, ifc_len, MBLKL(mp1));
232 kmem_free(lifc.lifc_buf, bufsize);
233 break;
234 }
235 case SIOCGIFMTU:
236 case SIOCGIFFLAGS:
237 err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (struct ifreq),
238 addr);
239 break;
240
241 case TI_GETMYNAME: {
242 rds_t *rds;
243 STRUCT_HANDLE(strbuf, sb);
244 ipaddr_t v4addr;
245 uint16_t port;
246 int addrlen;
247 sin_t *sin;
248
249 STRUCT_SET_HANDLE(sb,
250 ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag, addr);
251 rds = (rds_t *)q->q_ptr;
252 ASSERT(rds->rds_family == AF_INET_OFFLOAD);
253 addrlen = sizeof (sin_t);
254 v4addr = rds->rds_src;
255 port = rds->rds_port;
256 mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen,
257 B_TRUE);
258 if (mp1 == NULL)
259 return;
260 STRUCT_FSET(sb, len, (int)sizeof (sin_t));
261 sin = (sin_t *)(uintptr_t)mp1->b_rptr;
262 mp1->b_wptr = (uchar_t *)&sin[1];
263 *sin = sin_null;
264 sin->sin_family = AF_INET_OFFLOAD;
265 sin->sin_addr.s_addr = v4addr;
266 sin->sin_port = port;
267
268 }
269 break;
270 default:
271 err = EOPNOTSUPP;
272 break;
273 }
274 if (err == 0) {
275 mi_copyout(q, mp);
276 return;
277 }
278 done:
279 mi_copy_done(q, mp, err);
280 }
281
282 void
rds_ioctl_copyin_setup(queue_t * q,mblk_t * mp)283 rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp)
284 {
285 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
286 int copyin_size;
287
288 if (mp->b_cont == NULL) {
289 iocp->ioc_error = EINVAL;
290 mp->b_datap->db_type = M_IOCNAK;
291 iocp->ioc_count = 0;
292 qreply(q, mp);
293 return;
294 }
295
296 switch (iocp->ioc_cmd) {
297 case O_SIOCGIFCONF:
298 case SIOCGIFCONF:
299 if (iocp->ioc_count == TRANSPARENT)
300 copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag);
301 else
302 copyin_size = iocp->ioc_count;
303 break;
304
305 case SIOCGIFNUM:
306 copyin_size = sizeof (int);
307 break;
308 case SIOCGIFFLAGS:
309 case SIOCGIFMTU:
310 copyin_size = sizeof (struct ifreq);
311 break;
312 case TI_GETMYNAME:
313 copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag);
314 break;
315 }
316 mi_copyin(q, mp, NULL, copyin_size);
317 }
318
319 void
rds_ioctl(queue_t * q,mblk_t * mp)320 rds_ioctl(queue_t *q, mblk_t *mp)
321 {
322 struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
323
324
325 switch (iocp->ioc_cmd) {
326 case O_SIOCGIFCONF:
327 case SIOCGIFCONF:
328 case SIOCGIFNUM:
329 case SIOCGIFMTU:
330 case SIOCGIFFLAGS:
331 case TI_GETMYNAME:
332 rds_ioctl_copyin_setup(q, mp);
333 break;
334 default:
335 cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n");
336 miocnak(q, mp, 0, ENOTSUP);
337 break;
338 }
339 }
340
341 boolean_t
rds_verify_bind_address(ipaddr_t addr)342 rds_verify_bind_address(ipaddr_t addr)
343 {
344 int i, nifs;
345 uint_t bufsize;
346 struct lifconf lifc;
347 struct lifreq *lifrp;
348 struct sockaddr_in *sinp;
349 boolean_t retval = B_FALSE;
350
351 if (rds_do_lifconf(&lifc, &bufsize) != 0)
352 return (B_FALSE);
353
354 nifs = lifc.lifc_len / sizeof (struct lifreq);
355 for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) {
356 sinp = (struct sockaddr_in *)&lifrp->lifr_addr;
357 if (rds_capable_interface(lifrp) &&
358 sinp->sin_addr.s_addr == addr) {
359 retval = B_TRUE;
360 break;
361 }
362 }
363
364 kmem_free(lifc.lifc_buf, bufsize);
365 return (retval);
366 }
367