xref: /illumos-gate/usr/src/uts/common/io/ib/clients/rds/rds_ioctl.c (revision 721fffe35d40e548a5a58dc53a2ec9c6762172d9)
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
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
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 (ddi_parse(ifname, drv, &ppa) == DDI_SUCCESS &&
110 	    rds_transport_ops->rds_transport_if_lookup_by_name(drv));
111 }
112 
113 /*
114  * Issue an SIOCGLIFCONF down to IP and return the result in `lifcp'.
115  * lifcp->lifc_buf is dynamically allocated to be *bufsizep bytes.
116  */
117 static int
118 rds_do_lifconf(struct lifconf *lifcp, uint_t *bufsizep)
119 {
120 	int err;
121 	int nifs;
122 
123 	if ((err = rds_do_ip_ioctl(SIOCGIFNUM, sizeof (int), &nifs)) != 0)
124 		return (err);
125 
126 	/*
127 	 * Pad the interface count to account for additional interfaces that
128 	 * may have been configured between the SIOCGLIFNUM and SIOCGLIFCONF.
129 	 */
130 	nifs += 4;
131 
132 	bzero(lifcp, sizeof (struct lifconf));
133 	lifcp->lifc_family = AF_INET;
134 	lifcp->lifc_len = *bufsizep = (nifs * sizeof (struct lifreq));
135 	lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_NOSLEEP);
136 	if (lifcp->lifc_buf == NULL)
137 		return (ENOMEM);
138 
139 	err = rds_do_ip_ioctl(SIOCGLIFCONF, sizeof (struct lifconf), lifcp);
140 	if (err != 0) {
141 		kmem_free(lifcp->lifc_buf, *bufsizep);
142 		return (err);
143 	}
144 	return (0);
145 }
146 
147 void
148 rds_ioctl_copyin_done(queue_t *q, mblk_t *mp)
149 {
150 	void	*addr;
151 	mblk_t	*mp1;
152 	int	err = 0;
153 	struct	iocblk *iocp = (void *)mp->b_rptr;
154 
155 	if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) {
156 		err = EPROTO;
157 		goto done;
158 	}
159 
160 	addr = mp1->b_rptr;
161 
162 	switch (iocp->ioc_cmd) {
163 	case SIOCGIFNUM: {
164 		uint_t bufsize;
165 		struct lifconf lifc;
166 		struct lifreq *lifrp;
167 		int i, nifs, retval = 0;
168 
169 		if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0)
170 			break;
171 
172 		nifs = lifc.lifc_len / sizeof (struct lifreq);
173 		for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) {
174 			if (strlen(lifrp->lifr_name) <= IFNAMSIZ &&
175 			    rds_capable_interface(lifrp)) {
176 				retval++;
177 			}
178 		}
179 		*((int *)addr) = retval;
180 		kmem_free(lifc.lifc_buf, bufsize);
181 		break;
182 	}
183 
184 	case O_SIOCGIFCONF:
185 	case SIOCGIFCONF: {
186 		STRUCT_HANDLE(ifconf, ifc);
187 		caddr_t ubuf_addr;
188 		int	ubuf_size;
189 		uint_t	bufsize;
190 		int 	i, nifs;
191 		struct lifconf lifc;
192 		struct lifreq *lifrp;
193 		struct ifreq *ifrp;
194 
195 		STRUCT_SET_HANDLE(ifc, iocp->ioc_flag, (struct ifconf *)addr);
196 		ubuf_size = STRUCT_FGET(ifc, ifc_len);
197 		ubuf_addr = STRUCT_FGETP(ifc, ifc_buf);
198 
199 		if ((err = rds_do_lifconf(&lifc, &bufsize)) != 0)
200 			break;
201 
202 		mp1 = mi_copyout_alloc(q, mp, ubuf_addr, ubuf_size, B_FALSE);
203 		if (mp1 == NULL) {
204 			err = ENOMEM;
205 			kmem_free(lifc.lifc_buf, bufsize);
206 			break;
207 		}
208 
209 		ifrp = (void *)mp1->b_rptr;
210 		nifs = lifc.lifc_len / sizeof (struct lifreq);
211 		for (lifrp = lifc.lifc_req, i = 0; i < nifs &&
212 		    MBLKTAIL(mp1) >= sizeof (struct ifreq); i++, lifrp++) {
213 			/*
214 			 * Skip entries that are impossible to return with
215 			 * SIOCGIFCONF, or not RDS-capable.
216 			 */
217 			if (strlen(lifrp->lifr_name) > IFNAMSIZ ||
218 			    !rds_capable_interface(lifrp)) {
219 				continue;
220 			}
221 
222 			ifrp->ifr_addr = *(struct sockaddr *)&lifrp->lifr_addr;
223 			ifrp->ifr_addr.sa_family = AF_INET_OFFLOAD;
224 			(void) strlcpy(ifrp->ifr_name, lifrp->lifr_name,
225 			    IFNAMSIZ);
226 			ifrp++;
227 			mp1->b_wptr += sizeof (struct ifreq);
228 		}
229 
230 		STRUCT_FSET(ifc, ifc_len, MBLKL(mp1));
231 		kmem_free(lifc.lifc_buf, bufsize);
232 		break;
233 	}
234 	case SIOCGIFMTU:
235 	case SIOCGIFFLAGS:
236 		err = rds_do_ip_ioctl(iocp->ioc_cmd, sizeof (struct ifreq),
237 		    addr);
238 		break;
239 
240 	case TI_GETMYNAME: {
241 		rds_t *rds;
242 		STRUCT_HANDLE(strbuf, sb);
243 		ipaddr_t	v4addr;
244 		uint16_t port;
245 		int addrlen;
246 		sin_t *sin;
247 
248 		STRUCT_SET_HANDLE(sb,
249 		    ((struct iocblk *)(uintptr_t)mp->b_rptr)->ioc_flag, addr);
250 		rds = (rds_t *)q->q_ptr;
251 		ASSERT(rds->rds_family == AF_INET_OFFLOAD);
252 		addrlen = sizeof (sin_t);
253 		v4addr = rds->rds_src;
254 		port = rds->rds_port;
255 		mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen,
256 		    B_TRUE);
257 		if (mp1 == NULL)
258 			return;
259 		STRUCT_FSET(sb, len, (int)sizeof (sin_t));
260 		sin = (sin_t *)(uintptr_t)mp1->b_rptr;
261 		mp1->b_wptr = (uchar_t *)&sin[1];
262 		*sin = sin_null;
263 		sin->sin_family = AF_INET_OFFLOAD;
264 		sin->sin_addr.s_addr = v4addr;
265 		sin->sin_port = port;
266 
267 	}
268 		break;
269 	default:
270 		err = EOPNOTSUPP;
271 		break;
272 	}
273 	if (err == 0) {
274 		mi_copyout(q, mp);
275 		return;
276 	}
277 done:
278 	mi_copy_done(q, mp, err);
279 }
280 
281 void
282 rds_ioctl_copyin_setup(queue_t *q, mblk_t *mp)
283 {
284 	struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
285 	int	copyin_size;
286 
287 	if (mp->b_cont == NULL) {
288 		iocp->ioc_error = EINVAL;
289 		mp->b_datap->db_type = M_IOCNAK;
290 		iocp->ioc_count = 0;
291 		qreply(q, mp);
292 		return;
293 	}
294 
295 	switch (iocp->ioc_cmd) {
296 	case O_SIOCGIFCONF:
297 	case SIOCGIFCONF:
298 		if (iocp->ioc_count == TRANSPARENT)
299 			copyin_size = SIZEOF_STRUCT(ifconf, iocp->ioc_flag);
300 		else
301 			copyin_size = iocp->ioc_count;
302 		break;
303 
304 	case SIOCGIFNUM:
305 		copyin_size = sizeof (int);
306 		break;
307 	case SIOCGIFFLAGS:
308 	case SIOCGIFMTU:
309 		copyin_size = sizeof (struct ifreq);
310 		break;
311 	case TI_GETMYNAME:
312 		copyin_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag);
313 		break;
314 	}
315 	mi_copyin(q, mp, NULL, copyin_size);
316 }
317 
318 void
319 rds_ioctl(queue_t *q, mblk_t *mp)
320 {
321 	struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
322 
323 
324 	switch (iocp->ioc_cmd) {
325 		case O_SIOCGIFCONF:
326 		case SIOCGIFCONF:
327 		case SIOCGIFNUM:
328 		case SIOCGIFMTU:
329 		case SIOCGIFFLAGS:
330 		case TI_GETMYNAME:
331 			rds_ioctl_copyin_setup(q, mp);
332 			break;
333 		default:
334 			cmn_err(CE_CONT, "rds_wput unsupported IOCTL \n");
335 			miocnak(q, mp, 0, ENOTSUP);
336 			break;
337 	}
338 }
339 
340 boolean_t
341 rds_verify_bind_address(ipaddr_t addr)
342 {
343 	int i, nifs;
344 	uint_t bufsize;
345 	struct lifconf lifc;
346 	struct lifreq *lifrp;
347 	struct sockaddr_in *sinp;
348 	boolean_t retval = B_FALSE;
349 
350 	if (rds_do_lifconf(&lifc, &bufsize) != 0)
351 		return (B_FALSE);
352 
353 	nifs = lifc.lifc_len / sizeof (struct lifreq);
354 	for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) {
355 		sinp = (struct sockaddr_in *)&lifrp->lifr_addr;
356 		if (rds_capable_interface(lifrp) &&
357 		    sinp->sin_addr.s_addr == addr) {
358 			retval = B_TRUE;
359 			break;
360 		}
361 	}
362 
363 	kmem_free(lifc.lifc_buf, bufsize);
364 	return (retval);
365 }
366