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