xref: /illumos-gate/usr/src/uts/common/io/ib/clients/rds/rds_ioctl.c (revision b86efd96f8acd85ddaa930a2f0c1d664237e4aaf)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #define	AF_INET_OFFLOAD	30
29 
30 #include <sys/sockio.h>
31 #include <sys/stream.h>
32 #include <sys/errno.h>
33 #include <sys/cmn_err.h>
34 #include <sys/strsun.h>
35 #include <inet/common.h>
36 #include <net/if.h>
37 #include <inet/mi.h>
38 #include <sys/t_kuser.h>
39 #include <sys/stropts.h>
40 #include <sys/pathname.h>
41 #include <sys/kstr.h>
42 #include <sys/timod.h>
43 #include <sys/ib/clients/rds/rds.h>
44 #include <sys/ib/clients/rds/rds_transport.h>
45 
46 static	sin_t	sin_null;	/* Zero address for quick clears */
47 
48 #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
49 
50 #define	isalpha(ch)	(((ch) >= 'a' && (ch) <= 'z') || \
51 			((ch) >= 'A' && (ch) <= 'Z'))
52 
53 
54 /*
55  * Just pass the ioctl to IP and the result to the caller.
56  */
57 int
58 rds_do_ip_ioctl(int cmd, int len, caddr_t arg)
59 {
60 	vnode_t	*kvp, *vp;
61 	TIUSER	*tiptr;
62 	struct	strioctl iocb;
63 	int	err = 0;
64 
65 	if (lookupname("/dev/udp", UIO_SYSSPACE, FOLLOW, NULLVPP,
66 	    &kvp) == 0) {
67 		if (t_kopen((file_t *)NULL, kvp->v_rdev, FREAD|FWRITE,
68 		    &tiptr, CRED()) == 0) {
69 			vp = tiptr->fp->f_vnode;
70 		} else {
71 			VN_RELE(kvp);
72 			return (EPROTO);
73 		}
74 	} else {
75 			return (EPROTO);
76 	}
77 
78 	iocb.ic_cmd = cmd;
79 	iocb.ic_timout = 0;
80 	iocb.ic_len = len;
81 	iocb.ic_dp = arg;
82 	err = kstr_ioctl(vp, I_STR, (intptr_t)&iocb);
83 	(void) t_kclose(tiptr, 0);
84 	VN_RELE(kvp);
85 	return (err);
86 }
87 
88 static int
89 rds_dl_info(ldi_handle_t lh, dl_info_ack_t *info)
90 {
91 	dl_info_req_t *info_req;
92 	union DL_primitives *dl_prim;
93 	mblk_t *mp;
94 	int error;
95 
96 	if ((mp = allocb(sizeof (dl_info_req_t), BPRI_MED)) == NULL) {
97 		return (ENOMEM);
98 	}
99 
100 	mp->b_datap->db_type = M_PROTO;
101 
102 	info_req = (dl_info_req_t *)(uintptr_t)mp->b_wptr;
103 	mp->b_wptr += sizeof (dl_info_req_t);
104 	info_req->dl_primitive = DL_INFO_REQ;
105 
106 	if ((error = ldi_putmsg(lh, mp)) != 0) {
107 		return (error);
108 	}
109 	if ((error = ldi_getmsg(lh, &mp, (timestruc_t *)NULL)) != 0) {
110 		return (error);
111 	}
112 
113 	dl_prim = (union DL_primitives *)(uintptr_t)mp->b_rptr;
114 	switch (dl_prim->dl_primitive) {
115 	case DL_INFO_ACK:
116 		if (((uintptr_t)mp->b_wptr - (uintptr_t)mp->b_rptr) <
117 		    sizeof (dl_info_ack_t)) {
118 			error = -1;
119 		} else {
120 			*info = *(dl_info_ack_t *)(uintptr_t)mp->b_rptr;
121 			error = 0;
122 		}
123 		break;
124 	default:
125 		error = -1;
126 		break;
127 	}
128 
129 	freemsg(mp);
130 	return (error);
131 }
132 
133 
134 static boolean_t
135 rds_is_ib_interface(char *name)
136 {
137 
138 	char		dev_path[MAXPATHLEN];
139 	ldi_handle_t	lh;
140 	dl_info_ack_t	info;
141 	int		ret = 0;
142 	int		i = 0;
143 
144 	(void) strcpy(dev_path, "/dev/");
145 
146 	/*
147 	 * ibd devices are only style 2 devices
148 	 * so we will open only style 2 devices
149 	 * by ignoring the ppa
150 	 */
151 	while (isalpha(name[i])) {
152 		i++;
153 	}
154 
155 	if (i == 0) {
156 		/*
157 		 * null name.
158 		 */
159 		return (B_FALSE);
160 	}
161 
162 	if (strncmp("lo", name, i) == 0) {
163 		/*
164 		 * loopback interface is considered RDS capable
165 		 */
166 		return (B_TRUE);
167 	}
168 
169 	(void) strncat((dev_path + sizeof ("/dev/") -1), name, i);
170 
171 	ret = ldi_open_by_name(dev_path, FREAD|FWRITE, kcred, &lh, rds_li);
172 	if (ret != 0) {
173 		return (B_FALSE);
174 	}
175 
176 	ret = rds_dl_info(lh, &info);
177 
178 	(void) ldi_close(lh, FREAD|FWRITE, kcred);
179 
180 	if (ret != 0 || (info.dl_mac_type != DL_IB &&
181 	    !rds_transport_ops->rds_transport_if_lookup_by_name(name))) {
182 		return (B_FALSE);
183 	}
184 
185 	return (B_TRUE);
186 }
187 
188 void
189 rds_ioctl_copyin_done(queue_t *q, mblk_t *mp)
190 {
191 	char	*addr;
192 	mblk_t	*mp1;
193 	int	err = 0;
194 	struct	iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
195 
196 	if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) {
197 		err = EPROTO;
198 		goto done;
199 	}
200 
201 	addr = (char *)mp1->b_rptr;
202 
203 	switch (iocp->ioc_cmd) {
204 
205 	case SIOCGIFNUM: {
206 		/* Get number of interfaces. */
207 		struct ifconf   kifc;
208 		struct ifreq *ifr;
209 		int num_ifs;
210 		int n;
211 
212 		err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (int),
213 		    (char *)&num_ifs);
214 		if (err != 0) {
215 			break;
216 		}
217 
218 		kifc.ifc_len = num_ifs * sizeof (struct ifreq);
219 		kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP);
220 		err = rds_do_ip_ioctl(SIOCGIFCONF,
221 		    sizeof (struct ifconf), (caddr_t)&kifc);
222 		if (err != 0) {
223 			kmem_free(kifc.ifc_buf, kifc.ifc_len);
224 			break;
225 		}
226 		ifr = kifc.ifc_req;
227 		n = num_ifs;
228 		for (num_ifs = 0; n > 0; ifr++) {
229 			if (rds_is_ib_interface(ifr->ifr_name)) {
230 				num_ifs++;
231 			}
232 			n--;
233 		}
234 		*((int *)(uintptr_t)addr) = num_ifs;
235 		kmem_free(kifc.ifc_buf, kifc.ifc_len);
236 	}
237 		break;
238 
239 	case O_SIOCGIFCONF:
240 	case SIOCGIFCONF: {
241 		STRUCT_HANDLE(ifconf, ifc);
242 		caddr_t ubuf_addr;
243 		int	ubuf_size;
244 		struct ifconf   kifc;
245 		struct ifreq *ifr, *ptr;
246 		int num_ifs;
247 
248 		STRUCT_SET_HANDLE(ifc, iocp->ioc_flag,
249 		    (struct ifconf *)(uintptr_t)addr);
250 
251 		ubuf_size = STRUCT_FGET(ifc, ifc_len);
252 		ubuf_addr = STRUCT_FGETP(ifc, ifc_buf);
253 
254 		err = rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int),
255 		    (char *)&num_ifs);
256 		if (err != 0) {
257 			break;
258 		}
259 
260 		kifc.ifc_len = num_ifs * sizeof (struct ifreq);
261 		kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP);
262 		err = rds_do_ip_ioctl(iocp->ioc_cmd,
263 		    sizeof (struct ifconf), (caddr_t)&kifc);
264 		if (err != 0) {
265 			kmem_free(kifc.ifc_buf, kifc.ifc_len);
266 			break;
267 		}
268 		mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE);
269 		if (mp1 == NULL) {
270 			err = ENOMEM;
271 			kmem_free(kifc.ifc_buf, ubuf_size);
272 			break;
273 		}
274 
275 		ifr = kifc.ifc_req;
276 		ptr = (struct ifreq *)(uintptr_t)mp1->b_rptr;
277 		for (; num_ifs > 0 &&
278 		    (int)((uintptr_t)mp1->b_wptr - (uintptr_t)mp1->b_rptr) <
279 		    ubuf_size; num_ifs--, ifr++) {
280 			if (rds_is_ib_interface(ifr->ifr_name)) {
281 				ifr->ifr_addr.sa_family = AF_INET_OFFLOAD;
282 				bcopy((caddr_t)ifr, ptr, sizeof (struct ifreq));
283 				ptr++;
284 				mp1->b_wptr = (uchar_t *)ptr;
285 			}
286 		}
287 
288 		STRUCT_FSET(ifc, ifc_len, (int)((uintptr_t)mp1->b_wptr -
289 		    (uintptr_t)mp1->b_rptr));
290 
291 		kmem_free(kifc.ifc_buf, kifc.ifc_len);
292 	}
293 		break;
294 	case SIOCGIFMTU:
295 		err = rds_do_ip_ioctl(iocp->ioc_cmd,
296 		    sizeof (struct ifreq), addr);
297 		break;
298 
299 	case SIOCGIFFLAGS:
300 		err = rds_do_ip_ioctl(iocp->ioc_cmd,
301 		    sizeof (struct ifreq), addr);
302 		break;
303 	case TI_GETMYNAME: {
304 
305 		rds_t *rds;
306 		STRUCT_HANDLE(strbuf, sb);
307 		ipaddr_t	v4addr;
308 		uint16_t port;
309 		int addrlen;
310 		sin_t *sin;
311 
312 		STRUCT_SET_HANDLE(sb,
313 		    ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag,
314 		    (void *)(uintptr_t)addr);
315 		rds = (rds_t *)q->q_ptr;
316 		ASSERT(rds->rds_family == AF_INET_OFFLOAD);
317 		addrlen = sizeof (sin_t);
318 		v4addr = rds->rds_src;
319 		port = rds->rds_port;
320 		mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen,
321 		    B_TRUE);
322 		if (mp1 == NULL)
323 			return;
324 		STRUCT_FSET(sb, len, (int)sizeof (sin_t));
325 		sin = (sin_t *)(uintptr_t)mp1->b_rptr;
326 		mp1->b_wptr = (uchar_t *)&sin[1];
327 		*sin = sin_null;
328 		sin->sin_family = AF_INET_OFFLOAD;
329 		sin->sin_addr.s_addr = v4addr;
330 		sin->sin_port = port;
331 
332 	}
333 		break;
334 	default:
335 		err = EOPNOTSUPP;
336 		break;
337 	}
338 	if (err == 0) {
339 		mi_copyout(q, mp);
340 		return;
341 	}
342 done:
343 	mi_copy_done(q, mp, err);
344 }
345 
346 
347 void
348 rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp)
349 {
350 	struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
351 	int	copyin_size;
352 
353 	if (mp->b_cont == NULL) {
354 		iocp->ioc_error = EINVAL;
355 		mp->b_datap->db_type = M_IOCNAK;
356 		iocp->ioc_count = 0;
357 		qreply(q, mp);
358 		return;
359 	}
360 
361 	switch (iocp->ioc_cmd) {
362 	case O_SIOCGIFCONF:
363 	case SIOCGIFCONF:
364 		if (iocp->ioc_count == TRANSPARENT)
365 			copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag);
366 		else
367 			copyin_size = iocp->ioc_count;
368 		break;
369 
370 	case SIOCGIFNUM:
371 		copyin_size = sizeof (int);
372 		break;
373 	case SIOCGIFFLAGS:
374 	case SIOCGIFMTU:
375 		copyin_size = sizeof (struct ifreq);
376 		break;
377 	case TI_GETMYNAME:
378 		copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag);
379 		break;
380 	}
381 	mi_copyin(q, mp, NULL, copyin_size);
382 }
383 
384 void
385 rds_ioctl(queue_t *q, mblk_t *mp)
386 {
387 	struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
388 
389 
390 	switch (iocp->ioc_cmd) {
391 		case O_SIOCGIFCONF:
392 		case SIOCGIFCONF:
393 		case SIOCGIFNUM:
394 		case SIOCGIFMTU:
395 		case SIOCGIFFLAGS:
396 		case TI_GETMYNAME:
397 			rds_ioctl_copyin_setup(q, mp);
398 			break;
399 		default:
400 			cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n");
401 			miocnak(q, mp, 0, ENOTSUP);
402 			break;
403 	}
404 }
405 
406 boolean_t
407 rds_verify_bind_address(ipaddr_t addr)
408 {
409 	int	numifs;
410 	struct ifconf   kifc;
411 	struct ifreq *ifr;
412 	boolean_t ret = B_FALSE;
413 
414 
415 	if (rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), (caddr_t)&numifs)) {
416 		return (ret);
417 	}
418 
419 	kifc.ifc_len = numifs * sizeof (struct ifreq);
420 	kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP);
421 
422 	if (rds_do_ip_ioctl(SIOCGIFCONF, sizeof (struct ifconf),
423 	    (caddr_t)&kifc)) {
424 		goto done;
425 	}
426 
427 	ifr = kifc.ifc_req;
428 	for (numifs = kifc.ifc_len / sizeof (struct ifreq);
429 	    numifs > 0; numifs--, ifr++) {
430 		struct	sockaddr_in	*sin;
431 
432 		sin = (struct sockaddr_in *)(uintptr_t)&ifr->ifr_addr;
433 		if ((sin->sin_addr.s_addr == addr) &&
434 		    rds_is_ib_interface(ifr->ifr_name)) {
435 				ret = B_TRUE;
436 				break;
437 		}
438 	}
439 
440 done:
441 	kmem_free(kifc.ifc_buf, kifc.ifc_len);
442 	return (ret);
443 }
444