xref: /illumos-gate/usr/src/uts/common/io/ib/clients/rds/rds_ioctl.c (revision d62bc4badc1c1f1549c961cfb8b420e650e1272b)
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 2008 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  * Just pass the ioctl to IP and the result to the caller.
55  */
56 int
57 rds_do_ip_ioctl(int cmd, int len, caddr_t arg)
58 {
59 	vnode_t	*kvp, *vp;
60 	TIUSER	*tiptr;
61 	struct	strioctl iocb;
62 	k_sigset_t smask;
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 	sigintr(&smask, 0);
83 	err = kstr_ioctl(vp, I_STR, (intptr_t)&iocb);
84 	sigunintr(&smask);
85 	(void) t_kclose(tiptr, 0);
86 	VN_RELE(kvp);
87 	return (err);
88 }
89 
90 /*
91  * Return 0 if the interface is IB.
92  * Return error (>0) if any error is encountered during processing.
93  * Return -1 if the interface is not IB and no error.
94  */
95 static int
96 rds_is_ib_interface(char *name)
97 {
98 
99 	char		dev_path[MAXPATHLEN];
100 	char		devname[MAXNAMELEN];
101 	ldi_handle_t	lh;
102 	dl_info_ack_t	info;
103 	int		ret = 0;
104 	int		i;
105 	k_sigset_t	smask;
106 
107 	/*
108 	 * ibd devices are only style 2 devices
109 	 * so we will open only style 2 devices
110 	 * by ignoring the ppa
111 	 */
112 	i = strlen(name) - 1;
113 	while ((i >= 0) && (!isalpha(name[i]))) i--;
114 	if (i < 0) {
115 		/* Invalid interface name, no alphabet */
116 		return (-1);
117 	}
118 	(void) strncpy(devname, name, i + 1);
119 	devname[i + 1] = '\0';
120 
121 	if (strcmp("lo", devname) == 0) {
122 		/*
123 		 * loopback interface is considered RDS capable
124 		 */
125 		return (0);
126 	}
127 
128 	(void) strncpy(dev_path, "/dev/", MAXPATHLEN);
129 	if (strlcat(dev_path, devname, MAXPATHLEN) >= MAXPATHLEN) {
130 		/* string overflow */
131 		return (-1);
132 	}
133 
134 	ret = ldi_open_by_name(dev_path, FREAD|FWRITE, kcred, &lh, rds_li);
135 	if (ret != 0) {
136 		return (ret);
137 	}
138 
139 	sigintr(&smask, 0);
140 	ret = dl_info(lh, &info, NULL, NULL, NULL);
141 	sigunintr(&smask);
142 	(void) ldi_close(lh, FREAD|FWRITE, kcred);
143 	if (ret != 0) {
144 		return (ret);
145 	}
146 
147 	if (info.dl_mac_type != DL_IB &&
148 	    !rds_transport_ops->rds_transport_if_lookup_by_name(devname)) {
149 		return (-1);
150 	}
151 
152 	return (0);
153 }
154 
155 void
156 rds_ioctl_copyin_done(queue_t *q, mblk_t *mp)
157 {
158 	char	*addr;
159 	mblk_t	*mp1;
160 	int	err = 0;
161 	struct	iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
162 
163 	if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) {
164 		err = EPROTO;
165 		goto done;
166 	}
167 
168 	addr = (char *)mp1->b_rptr;
169 
170 	switch (iocp->ioc_cmd) {
171 
172 	case SIOCGIFNUM: {
173 		/* Get number of interfaces. */
174 		struct ifconf   kifc;
175 		struct ifreq *ifr;
176 		int num_ifs;
177 		int n;
178 
179 		err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (int),
180 		    (char *)&num_ifs);
181 		if (err != 0) {
182 			break;
183 		}
184 
185 		kifc.ifc_len = num_ifs * sizeof (struct ifreq);
186 		kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP);
187 		err = rds_do_ip_ioctl(SIOCGIFCONF,
188 		    sizeof (struct ifconf), (caddr_t)&kifc);
189 		if (err != 0) {
190 			kmem_free(kifc.ifc_buf, kifc.ifc_len);
191 			break;
192 		}
193 		ifr = kifc.ifc_req;
194 		n = num_ifs;
195 		for (num_ifs = 0; n > 0; ifr++) {
196 			err = rds_is_ib_interface(ifr->ifr_name);
197 			if (err == 0) {
198 				num_ifs++;
199 			} else if (err > 0) {
200 				num_ifs = 0;
201 				break;
202 			} else {
203 				err = 0;
204 			}
205 			n--;
206 		}
207 		*((int *)(uintptr_t)addr) = num_ifs;
208 		kmem_free(kifc.ifc_buf, kifc.ifc_len);
209 	}
210 		break;
211 
212 	case O_SIOCGIFCONF:
213 	case SIOCGIFCONF: {
214 		STRUCT_HANDLE(ifconf, ifc);
215 		caddr_t ubuf_addr;
216 		int	ubuf_size;
217 		struct ifconf   kifc;
218 		struct ifreq *ifr, *ptr;
219 		int num_ifs;
220 
221 		STRUCT_SET_HANDLE(ifc, iocp->ioc_flag,
222 		    (struct ifconf *)(uintptr_t)addr);
223 
224 		ubuf_size = STRUCT_FGET(ifc, ifc_len);
225 		ubuf_addr = STRUCT_FGETP(ifc, ifc_buf);
226 
227 		err = rds_do_ip_ioctl(SIOCGIFNUM, 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(iocp->ioc_cmd,
236 		    sizeof (struct ifconf), (caddr_t)&kifc);
237 		if (err != 0) {
238 			kmem_free(kifc.ifc_buf, kifc.ifc_len);
239 			break;
240 		}
241 		mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE);
242 		if (mp1 == NULL) {
243 			err = ENOMEM;
244 			kmem_free(kifc.ifc_buf, ubuf_size);
245 			break;
246 		}
247 
248 		ifr = kifc.ifc_req;
249 		ptr = (struct ifreq *)(uintptr_t)mp1->b_rptr;
250 		for (; num_ifs > 0 &&
251 		    (int)((uintptr_t)mp1->b_wptr - (uintptr_t)mp1->b_rptr) <
252 		    ubuf_size; num_ifs--, ifr++) {
253 			err = rds_is_ib_interface(ifr->ifr_name);
254 			if (err == 0) {
255 				ifr->ifr_addr.sa_family = AF_INET_OFFLOAD;
256 				bcopy((caddr_t)ifr, ptr, sizeof (struct ifreq));
257 				ptr++;
258 				mp1->b_wptr = (uchar_t *)ptr;
259 			} else if (err > 0) {
260 				break;
261 			} else {
262 				err = 0;
263 			}
264 		}
265 
266 		STRUCT_FSET(ifc, ifc_len, (int)((uintptr_t)mp1->b_wptr -
267 		    (uintptr_t)mp1->b_rptr));
268 		kmem_free(kifc.ifc_buf, kifc.ifc_len);
269 	}
270 		break;
271 	case SIOCGIFMTU:
272 		err = rds_do_ip_ioctl(iocp->ioc_cmd,
273 		    sizeof (struct ifreq), addr);
274 		break;
275 
276 	case SIOCGIFFLAGS:
277 		err = rds_do_ip_ioctl(iocp->ioc_cmd,
278 		    sizeof (struct ifreq), addr);
279 		break;
280 	case TI_GETMYNAME: {
281 
282 		rds_t *rds;
283 		STRUCT_HANDLE(strbuf, sb);
284 		ipaddr_t	v4addr;
285 		uint16_t port;
286 		int addrlen;
287 		sin_t *sin;
288 
289 		STRUCT_SET_HANDLE(sb,
290 		    ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag,
291 		    (void *)(uintptr_t)addr);
292 		rds = (rds_t *)q->q_ptr;
293 		ASSERT(rds->rds_family == AF_INET_OFFLOAD);
294 		addrlen = sizeof (sin_t);
295 		v4addr = rds->rds_src;
296 		port = rds->rds_port;
297 		mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen,
298 		    B_TRUE);
299 		if (mp1 == NULL)
300 			return;
301 		STRUCT_FSET(sb, len, (int)sizeof (sin_t));
302 		sin = (sin_t *)(uintptr_t)mp1->b_rptr;
303 		mp1->b_wptr = (uchar_t *)&sin[1];
304 		*sin = sin_null;
305 		sin->sin_family = AF_INET_OFFLOAD;
306 		sin->sin_addr.s_addr = v4addr;
307 		sin->sin_port = port;
308 
309 	}
310 		break;
311 	default:
312 		err = EOPNOTSUPP;
313 		break;
314 	}
315 	if (err == 0) {
316 		mi_copyout(q, mp);
317 		return;
318 	}
319 done:
320 	mi_copy_done(q, mp, err);
321 }
322 
323 
324 void
325 rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp)
326 {
327 	struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
328 	int	copyin_size;
329 
330 	if (mp->b_cont == NULL) {
331 		iocp->ioc_error = EINVAL;
332 		mp->b_datap->db_type = M_IOCNAK;
333 		iocp->ioc_count = 0;
334 		qreply(q, mp);
335 		return;
336 	}
337 
338 	switch (iocp->ioc_cmd) {
339 	case O_SIOCGIFCONF:
340 	case SIOCGIFCONF:
341 		if (iocp->ioc_count == TRANSPARENT)
342 			copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag);
343 		else
344 			copyin_size = iocp->ioc_count;
345 		break;
346 
347 	case SIOCGIFNUM:
348 		copyin_size = sizeof (int);
349 		break;
350 	case SIOCGIFFLAGS:
351 	case SIOCGIFMTU:
352 		copyin_size = sizeof (struct ifreq);
353 		break;
354 	case TI_GETMYNAME:
355 		copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag);
356 		break;
357 	}
358 	mi_copyin(q, mp, NULL, copyin_size);
359 }
360 
361 void
362 rds_ioctl(queue_t *q, mblk_t *mp)
363 {
364 	struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
365 
366 
367 	switch (iocp->ioc_cmd) {
368 		case O_SIOCGIFCONF:
369 		case SIOCGIFCONF:
370 		case SIOCGIFNUM:
371 		case SIOCGIFMTU:
372 		case SIOCGIFFLAGS:
373 		case TI_GETMYNAME:
374 			rds_ioctl_copyin_setup(q, mp);
375 			break;
376 		default:
377 			cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n");
378 			miocnak(q, mp, 0, ENOTSUP);
379 			break;
380 	}
381 }
382 
383 boolean_t
384 rds_verify_bind_address(ipaddr_t addr)
385 {
386 	int	numifs;
387 	struct ifconf   kifc;
388 	struct ifreq *ifr;
389 	boolean_t ret = B_FALSE;
390 
391 
392 	if (rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), (caddr_t)&numifs)) {
393 		return (ret);
394 	}
395 
396 	kifc.ifc_len = numifs * sizeof (struct ifreq);
397 	kifc.ifc_buf = kmem_zalloc(kifc.ifc_len, KM_SLEEP);
398 
399 	if (rds_do_ip_ioctl(SIOCGIFCONF, sizeof (struct ifconf),
400 	    (caddr_t)&kifc)) {
401 		goto done;
402 	}
403 
404 	ifr = kifc.ifc_req;
405 	for (numifs = kifc.ifc_len / sizeof (struct ifreq);
406 	    numifs > 0; numifs--, ifr++) {
407 		struct	sockaddr_in	*sin;
408 
409 		sin = (struct sockaddr_in *)(uintptr_t)&ifr->ifr_addr;
410 		if ((sin->sin_addr.s_addr == addr) &&
411 		    (rds_is_ib_interface(ifr->ifr_name) == 0)) {
412 				ret = B_TRUE;
413 				break;
414 		}
415 	}
416 
417 done:
418 	kmem_free(kifc.ifc_buf, kifc.ifc_len);
419 	return (ret);
420 }
421