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