xref: /illumos-gate/usr/src/uts/common/io/ib/mgt/ibcm/ibcm_arp.c (revision 261906274d77b4a1c6d61c75d170ab5a8e85a6a7)
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/types.h>
27 #include <sys/ddi.h>
28 #include <sys/sunddi.h>
29 #include <sys/strsubr.h>
30 #include <sys/socket.h>
31 #include <net/if_arp.h>
32 #include <net/if_types.h>
33 #include <sys/sockio.h>
34 #include <sys/pathname.h>
35 
36 #include <sys/ib/mgt/ibcm/ibcm_arp.h>
37 
38 #include <sys/kstr.h>
39 #include <sys/t_kuser.h>
40 
41 extern char cmlog[];
42 
43 extern int ibcm_resolver_pr_lookup(ibcm_arp_streams_t *ib_s,
44     ibt_ip_addr_t *dst_addr, ibt_ip_addr_t *src_addr, zoneid_t myzoneid);
45 extern void ibcm_arp_delete_prwqn(ibcm_arp_prwqn_t *wqnp);
46 
47 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibt_ip_addr_s))
48 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_ip_t))
49 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_ibd_insts_t))
50 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", ibcm_arp_prwqn_t))
51 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", sockaddr_in))
52 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", sockaddr_in6))
53 
54 int ibcm_printip = 0;
55 
56 /*
57  * Function:
58  *	ibcm_ip_print
59  * Input:
60  *	label		Arbitrary qualifying string
61  *	ipa		Pointer to IP Address to print
62  */
63 void
64 ibcm_ip_print(char *label, ibt_ip_addr_t *ipaddr)
65 {
66 	char    buf[INET6_ADDRSTRLEN];
67 
68 	if (ipaddr->family == AF_INET) {
69 		IBTF_DPRINTF_L2(cmlog, "%s: %s", label,
70 		    inet_ntop(AF_INET, &ipaddr->un.ip4addr, buf, sizeof (buf)));
71 	} else if (ipaddr->family == AF_INET6) {
72 		IBTF_DPRINTF_L2(cmlog, "%s: %s", label, inet_ntop(AF_INET6,
73 		    &ipaddr->un.ip6addr, buf, sizeof (buf)));
74 	} else {
75 		IBTF_DPRINTF_L2(cmlog, "%s: IP ADDR NOT SPECIFIED ", label);
76 	}
77 }
78 
79 
80 ibt_status_t
81 ibcm_arp_get_ibaddr(zoneid_t myzoneid, ibt_ip_addr_t srcaddr,
82     ibt_ip_addr_t destaddr, ib_gid_t *sgid, ib_gid_t *dgid,
83     ibt_ip_addr_t *saddrp)
84 {
85 	ibcm_arp_streams_t	*ib_s;
86 	ibcm_arp_prwqn_t	*wqnp;
87 	int			ret = 0;
88 	int			len;
89 
90 	IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibaddr(%d, %p, %p, %p, %p, %p)",
91 	    myzoneid, srcaddr, destaddr, sgid, dgid, saddrp);
92 
93 	ib_s = (ibcm_arp_streams_t *)kmem_zalloc(sizeof (ibcm_arp_streams_t),
94 	    KM_SLEEP);
95 
96 	mutex_init(&ib_s->lock, NULL, MUTEX_DEFAULT, NULL);
97 	cv_init(&ib_s->cv, NULL, CV_DRIVER, NULL);
98 
99 	mutex_enter(&ib_s->lock);
100 	ib_s->done = B_FALSE;
101 	mutex_exit(&ib_s->lock);
102 
103 	ret = ibcm_resolver_pr_lookup(ib_s, &destaddr, &srcaddr, myzoneid);
104 
105 	IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_ibaddr: ibcm_resolver_pr_lookup "
106 	    "returned: %d", ret);
107 	if (ret == 0) {
108 		mutex_enter(&ib_s->lock);
109 		while (ib_s->done != B_TRUE)
110 			cv_wait(&ib_s->cv, &ib_s->lock);
111 		mutex_exit(&ib_s->lock);
112 	}
113 
114 	mutex_enter(&ib_s->lock);
115 	wqnp = ib_s->wqnp;
116 	if (ib_s->status == 0) {
117 		if (sgid)
118 			*sgid = wqnp->sgid;
119 		if (dgid)
120 			*dgid = wqnp->dgid;
121 		/*
122 		 * If the user supplied a address, then verify we got
123 		 * for the same address.
124 		 */
125 		if (wqnp->usrc_addr.family && sgid) {
126 			len = (wqnp->usrc_addr.family == AF_INET) ?
127 			    IP_ADDR_LEN : sizeof (in6_addr_t);
128 			if (bcmp(&wqnp->usrc_addr.un,
129 			    &wqnp->src_addr.un, len)) {
130 				IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_ibaddr: "
131 				    "srcaddr mismatch");
132 
133 				/* Clean-up old data, and reset the done flag */
134 				ibcm_arp_delete_prwqn(wqnp);
135 				ib_s->done = B_FALSE;
136 				mutex_exit(&ib_s->lock);
137 
138 				ret = ibcm_resolver_pr_lookup(ib_s, &srcaddr,
139 				    &srcaddr, myzoneid);
140 				if (ret == 0) {
141 					mutex_enter(&ib_s->lock);
142 					while (ib_s->done != B_TRUE)
143 						cv_wait(&ib_s->cv, &ib_s->lock);
144 					mutex_exit(&ib_s->lock);
145 				}
146 				mutex_enter(&ib_s->lock);
147 				wqnp = ib_s->wqnp;
148 				if (ib_s->status == 0) {
149 					if (sgid)
150 						*sgid = wqnp->dgid;
151 
152 					if (saddrp)
153 						bcopy(&wqnp->src_addr, saddrp,
154 						    sizeof (ibt_ip_addr_t));
155 
156 					IBTF_DPRINTF_L4(cmlog,
157 					    "ibcm_arp_get_ibaddr: "
158 					    "SGID: %llX:%llX DGID: %llX:%llX",
159 					    sgid->gid_prefix, sgid->gid_guid,
160 					    dgid->gid_prefix, dgid->gid_guid);
161 
162 					ibcm_arp_delete_prwqn(wqnp);
163 				} else if (ret == 0) {
164 					if (wqnp)
165 						kmem_free(wqnp,
166 						    sizeof (ibcm_arp_prwqn_t));
167 				}
168 				goto arp_ibaddr_done;
169 			}
170 		}
171 
172 		if (saddrp)
173 			bcopy(&wqnp->src_addr, saddrp, sizeof (ibt_ip_addr_t));
174 
175 		IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibaddr: SGID: %llX:%llX"
176 		    " DGID: %llX:%llX", sgid->gid_prefix, sgid->gid_guid,
177 		    dgid->gid_prefix, dgid->gid_guid);
178 
179 		ibcm_arp_delete_prwqn(wqnp);
180 	} else if (ret == 0) {
181 		/*
182 		 * We come here only when lookup has returned empty (failed)
183 		 * via callback routine.
184 		 * i.e. ib_s->status is non-zero, while ret is zero.
185 		 */
186 		if (wqnp)
187 			kmem_free(wqnp, sizeof (ibcm_arp_prwqn_t));
188 	}
189 arp_ibaddr_done:
190 	ret = ib_s->status;
191 	mutex_exit(&ib_s->lock);
192 
193 arp_ibaddr_error:
194 
195 	mutex_destroy(&ib_s->lock);
196 	cv_destroy(&ib_s->cv);
197 	kmem_free(ib_s, sizeof (ibcm_arp_streams_t));
198 
199 	if (ret)
200 		return (IBT_FAILURE);
201 	else
202 		return (IBT_SUCCESS);
203 }
204 
205 
206 static int
207 ibcm_arp_get_ibd_insts_cb(dev_info_t *dip, void *arg)
208 {
209 	ibcm_arp_ibd_insts_t *ibds = (ibcm_arp_ibd_insts_t *)arg;
210 	ibcm_arp_ip_t	*ipp;
211 	ib_pkey_t	pkey;
212 	uint8_t		port;
213 	ib_guid_t	hca_guid;
214 	ib_gid_t	port_gid;
215 
216 	if (i_ddi_devi_attached(dip) &&
217 	    (strcmp(ddi_node_name(dip), "ibport") == 0) &&
218 	    (strstr(ddi_get_name_addr(dip), "ipib") != NULL)) {
219 
220 		if (ibds->ibcm_arp_ibd_cnt >= ibds->ibcm_arp_ibd_alloc) {
221 			ibcm_arp_ip_t	*tmp = NULL;
222 			uint8_t		new_count;
223 
224 			new_count = ibds->ibcm_arp_ibd_alloc +
225 			    IBCM_ARP_IBD_INSTANCES;
226 
227 			tmp = (ibcm_arp_ip_t *)kmem_zalloc(
228 			    new_count * sizeof (ibcm_arp_ip_t), KM_SLEEP);
229 			bcopy(ibds->ibcm_arp_ip, tmp,
230 			    ibds->ibcm_arp_ibd_alloc * sizeof (ibcm_arp_ip_t));
231 			kmem_free(ibds->ibcm_arp_ip,
232 			    ibds->ibcm_arp_ibd_alloc * sizeof (ibcm_arp_ip_t));
233 			ibds->ibcm_arp_ibd_alloc = new_count;
234 			ibds->ibcm_arp_ip = tmp;
235 		}
236 
237 		if (((hca_guid = ddi_prop_get_int64(DDI_DEV_T_ANY, dip, 0,
238 		    "hca-guid", 0)) == 0) ||
239 		    ((port = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
240 		    "port-number", 0)) == 0) ||
241 		    (ibt_get_port_state_byguid(hca_guid, port, &port_gid,
242 		    NULL) != IBT_SUCCESS) ||
243 		    ((pkey = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
244 		    "port-pkey", IB_PKEY_INVALID_LIMITED)) <=
245 		    IB_PKEY_INVALID_FULL)) {
246 			return (DDI_WALK_CONTINUE);
247 		}
248 
249 		ipp = &ibds->ibcm_arp_ip[ibds->ibcm_arp_ibd_cnt];
250 		ipp->ip_inst = ddi_get_instance(dip);
251 		ipp->ip_pkey = pkey;
252 		ipp->ip_hca_guid = hca_guid;
253 		ipp->ip_port_gid = port_gid;
254 		ibds->ibcm_arp_ibd_cnt++;
255 	}
256 	return (DDI_WALK_CONTINUE);
257 }
258 
259 static void
260 ibcm_arp_get_ibd_insts(ibcm_arp_ibd_insts_t *ibds)
261 {
262 	ddi_walk_devs(ddi_root_node(), ibcm_arp_get_ibd_insts_cb, ibds);
263 }
264 
265 /*
266  * Issue an ioctl down to IP.  There are several similar versions of this
267  * function (e.g., rpcib_do_ip_ioctl()); clearly a utility routine is needed.
268  */
269 static int
270 ibcm_do_ip_ioctl(int cmd, int len, void *arg)
271 {
272 	vnode_t *kkvp;
273 	TIUSER  *tiptr;
274 	struct  strioctl iocb;
275 	int	err = 0;
276 
277 	if (lookupname("/dev/udp", UIO_SYSSPACE, FOLLOW, NULLVPP, &kkvp) != 0)
278 		return (EPROTO);
279 
280 	if (t_kopen(NULL, kkvp->v_rdev, FREAD|FWRITE, &tiptr, CRED()) != 0) {
281 		VN_RELE(kkvp);
282 		return (EPROTO);
283 	}
284 
285 	iocb.ic_cmd = cmd;
286 	iocb.ic_timout = 0;
287 	iocb.ic_len = len;
288 	iocb.ic_dp = (caddr_t)arg;
289 	err = kstr_ioctl(tiptr->fp->f_vnode, I_STR, (intptr_t)&iocb);
290 	(void) t_kclose(tiptr, 0);
291 	VN_RELE(kkvp);
292 	return (err);
293 }
294 
295 /*
296  * Issue an SIOCGLIFCONF down to IP and return the result in `lifcp'.
297  * lifcp->lifc_buf is dynamically allocated to be *bufsizep bytes.
298  */
299 static int
300 ibcm_do_lifconf(struct lifconf *lifcp, uint_t *bufsizep, sa_family_t family_loc)
301 {
302 	int err;
303 	struct lifnum lifn;
304 
305 	bzero(&lifn, sizeof (struct lifnum));
306 	lifn.lifn_family = family_loc;
307 
308 	err = ibcm_do_ip_ioctl(SIOCGLIFNUM, sizeof (struct lifnum), &lifn);
309 	if (err != 0)
310 		return (err);
311 
312 	IBTF_DPRINTF_L4(cmlog, "ibcm_do_lifconf: Family %d, lifn_count %d",
313 	    family_loc, lifn.lifn_count);
314 	/*
315 	 * Pad the interface count to account for additional interfaces that
316 	 * may have been configured between the SIOCGLIFNUM and SIOCGLIFCONF.
317 	 */
318 	lifn.lifn_count += 4;
319 
320 	bzero(lifcp, sizeof (struct lifconf));
321 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*lifcp))
322 	lifcp->lifc_family = family_loc;
323 	lifcp->lifc_len = *bufsizep = lifn.lifn_count * sizeof (struct lifreq);
324 	lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_SLEEP);
325 
326 	err = ibcm_do_ip_ioctl(SIOCGLIFCONF, sizeof (struct lifconf), lifcp);
327 	if (err != 0) {
328 		kmem_free(lifcp->lifc_buf, *bufsizep);
329 		return (err);
330 	}
331 	return (0);
332 }
333 
334 /*
335  * Fill in `ibds' with IP addresses tied to IFT_IB IP interfaces.  Returns
336  * B_TRUE if at least one address was filled in.
337  */
338 static boolean_t
339 ibcm_arp_get_ibd_ipaddr(ibcm_arp_ibd_insts_t *ibds, sa_family_t family_loc)
340 {
341 	int i, nifs, naddr = 0;
342 	uint_t bufsize;
343 	struct lifconf lifc;
344 	struct lifreq *lifrp;
345 	ibcm_arp_ip_t *ipp;
346 
347 	if (ibcm_do_lifconf(&lifc, &bufsize, family_loc) != 0)
348 		return (B_FALSE);
349 
350 	nifs = lifc.lifc_len / sizeof (struct lifreq);
351 
352 	IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibd_ipaddr: Family %d, nifs %d",
353 	    family_loc, nifs);
354 
355 	for (lifrp = lifc.lifc_req, i = 0;
356 	    i < nifs && naddr < ibds->ibcm_arp_ibd_cnt; i++, lifrp++) {
357 		if (lifrp->lifr_type != IFT_IB)
358 			continue;
359 
360 		ipp = &ibds->ibcm_arp_ip[naddr];
361 		switch (lifrp->lifr_addr.ss_family) {
362 		case AF_INET:
363 			ipp->ip_inet_family = AF_INET;
364 			bcopy(&lifrp->lifr_addr, &ipp->ip_cm_sin,
365 			    sizeof (struct sockaddr_in));
366 			naddr++;
367 			break;
368 		case AF_INET6:
369 			ipp->ip_inet_family = AF_INET6;
370 			bcopy(&lifrp->lifr_addr, &ipp->ip_cm_sin6,
371 			    sizeof (struct sockaddr_in6));
372 			naddr++;
373 			break;
374 		}
375 	}
376 
377 	kmem_free(lifc.lifc_buf, bufsize);
378 	return (naddr > 0);
379 }
380 
381 ibt_status_t
382 ibcm_arp_get_ibds(ibcm_arp_ibd_insts_t *ibdp, sa_family_t family_loc)
383 {
384 #ifdef DEBUG
385 	int i;
386 #endif
387 
388 	IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds(%p)", ibdp);
389 
390 	ibcm_arp_get_ibd_insts(ibdp);
391 
392 	IBTF_DPRINTF_L3(cmlog, "ibcm_arp_get_ibds: Found %d ibd instances",
393 	    ibdp->ibcm_arp_ibd_cnt);
394 
395 	if (ibdp->ibcm_arp_ibd_cnt == 0)
396 		return (IBT_SRC_IP_NOT_FOUND);
397 
398 	/* Get the IP addresses of active ports. */
399 	if (!ibcm_arp_get_ibd_ipaddr(ibdp, family_loc)) {
400 		IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_ibds: failed to get "
401 		    "ibd instance: IBT_SRC_IP_NOT_FOUND");
402 		return (IBT_SRC_IP_NOT_FOUND);
403 	}
404 
405 #ifdef DEBUG
406 	for (i = 0; i < ibdp->ibcm_arp_ibd_cnt; i++) {
407 		char    my_buf[INET6_ADDRSTRLEN];
408 		ibcm_arp_ip_t	*aip = &ibdp->ibcm_arp_ip[i];
409 
410 		IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: ibd[%d]: Family %d "
411 		    "Instance %d PKey 0x%lX \n HCAGUID 0x%llX SGID %llX:%llX",
412 		    i, aip->ip_inet_family, aip->ip_inst, aip->ip_pkey,
413 		    aip->ip_hca_guid, aip->ip_port_gid.gid_prefix,
414 		    aip->ip_port_gid.gid_guid);
415 		if (aip->ip_inet_family == AF_INET) {
416 			IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: IPV4: %s",
417 			    inet_ntop(AF_INET, &aip->ip_cm_sin.sin_addr, my_buf,
418 			    sizeof (my_buf)));
419 		} else if (aip->ip_inet_family == AF_INET6) {
420 			IBTF_DPRINTF_L4(cmlog, "ibcm_arp_get_ibds: IPV6: %s",
421 			    inet_ntop(AF_INET6, &aip->ip_cm_sin6.sin6_addr,
422 			    my_buf, sizeof (my_buf)));
423 		} else {
424 			IBTF_DPRINTF_L2(cmlog, "ibcm_arp_get_ibds: Unknown "
425 			    "Family %d", aip->ip_inet_family);
426 		}
427 	}
428 #endif
429 
430 	return (IBT_SUCCESS);
431 }
432