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