xref: /illumos-gate/usr/src/lib/udapl/udapl_tavor/common/dapl_name_service.c (revision 1cfa752f4e24c34133009b0f6c139127a5c461de)
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 /*
23  * Copyright (c) 2002-2003, Network Appliance, Inc. All rights reserved.
24  */
25 
26 /*
27  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
28  */
29 
30 /*
31  *
32  * MODULE: dapl_name_service.c
33  *
34  * PURPOSE: Provide simple, file base name services in the absence
35  *	    of DNS hooks for a particular transport type. If an
36  *	    InfiniBand implementation supports IPoIB, this should
37  *	    not be used.
38  *
39  * Description: Interfaces in this file are completely described in
40  *		dapl_name_service.h
41  */
42 
43 /*
44  * Include files for setting up a network name
45  */
46 #include "dapl.h"
47 #include "dapl_name_service.h"
48 
49 #include <netinet/in.h>
50 #include <sys/sockio.h>
51 #include <net/if.h>
52 #include <net/if_dl.h>
53 #include <net/if_arp.h>
54 #include <net/if_types.h>
55 #include <arpa/inet.h>
56 #include <poll.h>
57 #include <ibd/ibd.h>
58 
59 #ifdef IBHOSTS_NAMING
60 #define	MAP_FILE		"/etc/dapl/ibhosts"
61 #define	MAX_GID_ENTRIES		32
62 DAPL_GID_MAP			g_gid_map_table[MAX_GID_ENTRIES];
63 
64 DAT_RETURN dapli_ns_create_gid_map(void);
65 DAT_RETURN dapli_ns_add_address(IN DAPL_GID_MAP	*gme);
66 #endif /* IBHOSTS_NAMING */
67 
68 /*
69  * dapls_ns_init
70  *
71  * Initialize naming services
72  *
73  * Input:
74  *	none
75  *
76  * Output:
77  * 	none
78  *
79  * Returns:
80  * 	DAT_SUCCESS
81  *	DAT_INVALID_PARAMETER
82  */
83 DAT_RETURN
dapls_ns_init(void)84 dapls_ns_init(void)
85 {
86 	DAT_RETURN	dat_status;
87 
88 	dat_status = DAT_SUCCESS;
89 #ifdef IBHOSTS_NAMING
90 	dat_status = dapli_ns_create_gid_map();
91 #endif /* IBHOSTS_NAMING */
92 
93 	return (dat_status);
94 }
95 
96 #ifdef IBHOSTS_NAMING
97 /*
98  * dapls_create_gid_map()
99  *
100  * Read /usr/local/etc/ibhosts to obtain host names and GIDs.
101  * Create a table containing IP addresses and GIDs which can
102  * be used for lookups.
103  *
104  * This implementation is a simple method providing name services
105  * when more advanced mechanisms do not exist. The proper way
106  * to obtain these mappings is to use a name service such as is
107  * provided by IPoIB on InfiniBand.
108  *
109  * Input:
110  *	device_name		Name of device as reported by the provider
111  *
112  * Output:
113  * 	none
114  *
115  * Returns:
116  * 	char * to string number
117  */
118 DAT_RETURN
dapli_ns_create_gid_map(void)119 dapli_ns_create_gid_map(void)
120 {
121 	FILE			*f;
122 	ib_gid_t		gid;
123 	char			hostname[128];
124 	int			rc;
125 	struct addrinfo		*addr;
126 	struct sockaddr_in	*si;
127 	DAPL_GID_MAP		gmt;
128 
129 	f = fopen(MAP_FILE, "r");
130 	if (f == NULL) {
131 		dapl_dbg_log(DAPL_DBG_TYPE_ERR, "ERROR: Must have file <%s> "
132 		    "for IP/GID mappings\n", MAP_FILE);
133 		return (DAT_ERROR(DAT_INTERNAL_ERROR, 0));
134 	}
135 
136 	rc = fscanf(f, "%s " F64x " " F64x, hostname,
137 	    &gid.gid_prefix, &gid.gid_guid);
138 	while (rc != EOF) {
139 		rc = dapls_osd_getaddrinfo(hostname, &addr);
140 
141 		if (rc != 0) {
142 			/*
143 			 * hostname not registered in DNS,
144 			 * provide a dummy value
145 			 */
146 			dapl_dbg_log(DAPL_DBG_TYPE_ERR,
147 			    "WARNING: <%s> not registered in "
148 			    "DNS, using dummy IP value\n", hostname);
149 			gmt.ip_address = 0x01020304;
150 		} else {
151 			/*
152 			 * Load into the ip/gid mapping table
153 			 */
154 			si = (struct sockaddr_in *)addr->ai_addr;
155 			if (AF_INET == addr->ai_addr->sa_family) {
156 				gmt.ip_address = si->sin_addr.s_addr;
157 			} else {
158 				dapl_dbg_log(DAPL_DBG_TYPE_ERR,
159 				    "WARNING: <%s> Address family "
160 				    "not supported, using dummy "
161 				    "IP value\n", hostname);
162 				gmt.ip_address = 0x01020304;
163 			}
164 			dapls_osd_freeaddrinfo(addr);
165 		}
166 		gmt.gid.gid_prefix = gid.gid_prefix;
167 		gmt.gid.gid_guid = gid.gid_guid;
168 
169 		dapli_ns_add_address(&gmt);
170 		rc = fscanf(f, "%s " F64x " " F64x, hostname,
171 		    &gid.gid_prefix, &gid.gid_guid);
172 	}
173 	(void) fclose(f);
174 	return (DAT_SUCCESS);
175 }
176 
177 /*
178  * dapli_ns_add_address
179  *
180  * Add a table entry to the  gid_map_table.
181  *
182  * Input:
183  *	remote_ia_address	remote IP address
184  *	gid			pointer to output gid
185  *
186  * Output:
187  * 	gid			filled in GID
188  *
189  * Returns:
190  * 	DAT_SUCCESS
191  *	DAT_INSUFFICIENT_RESOURCES
192  *	DAT_INVALID_PARAMETER
193  */
194 DAT_RETURN
dapli_ns_add_address(IN DAPL_GID_MAP * gme)195 dapli_ns_add_address(
196 	IN DAPL_GID_MAP	*gme)
197 {
198 	DAPL_GID_MAP	*gmt;
199 	int		count;
200 
201 	gmt = g_gid_map_table;
202 	for (count = 0, gmt = g_gid_map_table; gmt->ip_address; gmt++) {
203 		count++;
204 	}
205 	if (count > MAX_GID_ENTRIES) {
206 		return (DAT_ERROR(DAT_INSUFFICIENT_RESOURCES, 0));
207 	}
208 
209 	*gmt = *gme;
210 	return (DAT_SUCCESS);
211 }
212 
213 /*
214  * dapls_ns_lookup_address
215  *
216  * Look up the provided IA_ADDRESS in the gid_map_table. Return
217  * the gid if found.
218  *
219  * Input:
220  *	remote_ia_address	remote IP address
221  *	gid			pointer to output gid
222  *	timeout			timeout in microseconds
223  *
224  * Output:
225  * 	gid			filled in GID
226  *
227  * Returns:
228  * 	DAT_SUCCESS
229  *	DAT_INSUFFICIENT_RESOURCES
230  *	DAT_INVALID_PARAMETER
231  */
232 DAT_RETURN
dapls_ns_lookup_address(IN DAPL_IA * ia_ptr,IN DAT_IA_ADDRESS_PTR remote_ia_address,IN DAT_TIMEOUT timeout,OUT ib_gid_t * gid)233 dapls_ns_lookup_address(
234 	IN  DAPL_IA			*ia_ptr,
235 	IN  DAT_IA_ADDRESS_PTR		remote_ia_address,
236 	IN  DAT_TIMEOUT			timeout,
237 	OUT ib_gid_t			*gid)
238 {
239 	DAPL_GID_MAP		*gmt;
240 	struct sockaddr_in	*si;
241 
242 	/* unused here */
243 	ia_ptr = ia_ptr;
244 	si = (struct sockaddr_in *)remote_ia_address;
245 
246 	for (gmt = g_gid_map_table; gmt->ip_address; gmt++) {
247 		if (gmt->ip_address == si->sin_addr.s_addr) {
248 			gid->gid_guid = gmt->gid.gid_guid;
249 			gid->gid_prefix = gmt->gid.gid_prefix;
250 			return (DAT_SUCCESS);
251 		}
252 	}
253 	return (DAT_ERROR(DAT_INVALID_PARAMETER, 0));
254 }
255 #endif /* IBHOSTS_NAMING */
256 
257 /*
258  * utility function for printing a socket
259  */
260 char *
dapls_inet_ntop(struct sockaddr * addr,char * buf,size_t len)261 dapls_inet_ntop(struct sockaddr *addr, char *buf, size_t len)
262 {
263 	void	*addr_ptr;
264 
265 	if (addr->sa_family == AF_INET) {
266 		/* LINTED: E_BAD_PTR_CAST_ALIGN */
267 		addr_ptr = (void *)&((struct sockaddr_in *)addr)->sin_addr;
268 	} else if (addr->sa_family == AF_INET6) {
269 		/* LINTED: E_BAD_PTR_CAST_ALIGN */
270 		addr_ptr = (void *)&((struct sockaddr_in6 *)addr)->sin6_addr;
271 	} else {
272 		if (len > strlen("bad address")) {
273 			(void) sprintf(buf, "bad address");
274 		}
275 		return (buf);
276 	}
277 	return ((char *)inet_ntop(addr->sa_family, addr_ptr, buf, len));
278 }
279 
280 /*
281  * dapls_ns_lookup_address
282  *
283  * translates an IP address into a GID
284  *
285  * Input:
286  * 	ia_ptr			pointer to IA object
287  *	remote_ia_address	remote IP address
288  *	gid			pointer to output gid
289  *	timeout			timeout in microseconds
290  *
291  * Output:
292  * 	gid			filled in GID
293  *
294  * Returns:
295  * 	DAT_SUCCESS
296  *	DAT_INVALID_ADDRRESS
297  *	DAT_INVALID_PARAMETER
298  *	DAT_INTERNAL_ERROR
299  */
300 
301 #define	NS_MAX_RETRIES	60
302 
303 DAT_RETURN
304 dapls_ns_lookup_v4(
305 	IN  DAPL_IA			*ia_ptr,
306 	IN  struct sockaddr_in		*addr,
307 	IN  DAT_TIMEOUT			timeout,
308 	OUT ib_gid_t			*gid);
309 DAT_RETURN
310 dapls_ns_lookup_v6(
311 	IN  DAPL_IA			*ia_ptr,
312 	IN  struct sockaddr_in6		*addr,
313 	IN  DAT_TIMEOUT			timeout,
314 	OUT ib_gid_t			*gid);
315 
316 static int dapls_ns_subnet_match_v4(int s, DAPL_IA *ia_ptr,
317     struct sockaddr_in *addr);
318 static int dapls_ns_subnet_match_v6(int s, DAPL_IA *ia_ptr,
319     struct sockaddr_in6 *addr);
320 
321 static int dapls_ns_send_packet_v6(int s, struct sockaddr_in6 *addr);
322 static int dapls_ns_resolve_addr(int af, struct sockaddr *addr,
323     DAT_TIMEOUT timeout);
324 
325 DAT_RETURN
dapls_ns_lookup_address(IN DAPL_IA * ia_ptr,IN DAT_IA_ADDRESS_PTR remote_ia_address,IN DAT_TIMEOUT timeout,OUT ib_gid_t * gid)326 dapls_ns_lookup_address(
327 	IN  DAPL_IA			*ia_ptr,
328 	IN  DAT_IA_ADDRESS_PTR		remote_ia_address,
329 	IN  DAT_TIMEOUT			timeout,
330 	OUT ib_gid_t			*gid)
331 {
332 	DAT_RETURN		dat_status;
333 	struct sockaddr		*sock = (struct sockaddr *)remote_ia_address;
334 
335 	if (sock->sa_family == AF_INET) {
336 		dat_status = dapls_ns_lookup_v4(ia_ptr,
337 		    /* LINTED: E_BAD_PTR_CAST_ALIGN */
338 		    (struct sockaddr_in *)sock, timeout, gid);
339 	} else if (sock->sa_family == AF_INET6) {
340 		dat_status = dapls_ns_lookup_v6(ia_ptr,
341 		    /* LINTED: E_BAD_PTR_CAST_ALIGN */
342 		    (struct sockaddr_in6 *)sock, timeout, gid);
343 	} else {
344 		dat_status = DAT_INVALID_PARAMETER;
345 	}
346 	return (dat_status);
347 }
348 
349 DAT_RETURN
dapls_ns_lookup_v4(IN DAPL_IA * ia_ptr,IN struct sockaddr_in * addr,IN DAT_TIMEOUT timeout,OUT ib_gid_t * gid)350 dapls_ns_lookup_v4(
351 	IN  DAPL_IA			*ia_ptr,
352 	IN  struct sockaddr_in		*addr,
353 	IN  DAT_TIMEOUT			timeout,
354 	OUT ib_gid_t			*gid)
355 {
356 	struct xarpreq		ar;
357 	struct sockaddr_in	*sin;
358 	uchar_t			*mac;
359 	int			s, retries = 0;
360 
361 	(void) dapl_os_memzero(&ar, sizeof (ar));
362 	sin = (struct sockaddr_in *)&ar.xarp_pa;
363 	sin->sin_family = AF_INET;
364 	sin->sin_addr.s_addr = addr->sin_addr.s_addr;
365 	ar.xarp_ha.sdl_family = AF_LINK;
366 
367 	s = socket(AF_INET, SOCK_DGRAM, 0);
368 	if (s < 0) {
369 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
370 		    "ns_lookup_v4: socket: %s\n", strerror(errno));
371 		return (DAT_INTERNAL_ERROR);
372 	}
373 	if (dapls_ns_subnet_match_v4(s, ia_ptr, addr) != 0) {
374 		(void) close(s);
375 		return (DAT_INVALID_ADDRESS);
376 	}
377 again:;
378 	if (ioctl(s, SIOCGXARP, (caddr_t)&ar) < 0) {
379 		/*
380 		 * if SIOCGXARP failed, we force the ARP
381 		 * cache to be filled by connecting to the
382 		 * destination IP address.
383 		 */
384 		if (retries <= NS_MAX_RETRIES &&
385 		    dapls_ns_resolve_addr(AF_INET, (struct sockaddr *)addr,
386 		    timeout) == 0) {
387 			retries++;
388 			goto again;
389 		}
390 		dapl_dbg_log(DAPL_DBG_TYPE_ERR, "ns_lookup_v4: giving up\n");
391 		(void) close(s);
392 		return (DAT_ERROR(DAT_INVALID_ADDRESS,
393 		    DAT_INVALID_ADDRESS_UNREACHABLE));
394 	}
395 	if ((ar.xarp_flags & ATF_COM) == 0 &&
396 	    ar.xarp_ha.sdl_type == IFT_IB && retries <= NS_MAX_RETRIES) {
397 		/*
398 		 * we get here if arp resolution is still incomplete
399 		 */
400 		retries++;
401 		(void) sleep(1);
402 		goto again;
403 	}
404 	(void) close(s);
405 
406 	mac = (uchar_t *)LLADDR(&ar.xarp_ha);
407 	if (ar.xarp_flags & ATF_COM &&
408 	    ar.xarp_ha.sdl_type == IFT_IB &&
409 	    ar.xarp_ha.sdl_alen >= sizeof (ipoib_mac_t)) {
410 		ib_gid_t tmp_gid;
411 
412 		/* LINTED: E_BAD_PTR_CAST_ALIGN */
413 		(void) dapl_os_memcpy(&tmp_gid,
414 		    &((ipoib_mac_t *)mac)->ipoib_gidpref, sizeof (ib_gid_t));
415 		/*
416 		 * gids from the ARP table are in network order, convert
417 		 * the gids from network order to host byte order
418 		 */
419 		gid->gid_prefix = BETOH_64(tmp_gid.gid_prefix);
420 		gid->gid_guid = BETOH_64(tmp_gid.gid_guid);
421 	} else {
422 		int i, len;
423 
424 		len = ar.xarp_ha.sdl_alen;
425 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
426 		    "ns_lookup_v4: failed, non IB address: "
427 		    "len = %d, addr = 0x", len);
428 		if (len > 0) {
429 			for (i = 0; i < len; i++) {
430 				dapl_dbg_log(DAPL_DBG_TYPE_ERR,
431 				    "%02x", (int)mac[i] & 0xff);
432 			}
433 		} else {
434 			dapl_dbg_log(DAPL_DBG_TYPE_ERR, "0");
435 		}
436 		dapl_dbg_log(DAPL_DBG_TYPE_ERR, "\n");
437 		return (DAT_INVALID_ADDRESS);
438 	}
439 	return (DAT_SUCCESS);
440 }
441 
442 DAT_RETURN
dapls_ns_lookup_v6(IN DAPL_IA * ia_ptr,IN struct sockaddr_in6 * addr,IN DAT_TIMEOUT timeout,OUT ib_gid_t * gid)443 dapls_ns_lookup_v6(
444 	IN  DAPL_IA			*ia_ptr,
445 	IN  struct sockaddr_in6		*addr,
446 	IN  DAT_TIMEOUT			timeout,
447 	OUT ib_gid_t			*gid)
448 {
449 	struct lifreq		lifr;
450 	uchar_t			*mac;
451 	int			s, retries = 0;
452 
453 	s = socket(AF_INET6, SOCK_DGRAM, 0);
454 	if (s < 0) {
455 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
456 		    "ns_lookup_v6: socket: %s\n", strerror(errno));
457 		return (DAT_INTERNAL_ERROR);
458 	}
459 	if (dapls_ns_subnet_match_v6(s, ia_ptr, addr) != 0) {
460 		(void) close(s);
461 		return (DAT_INVALID_ADDRESS);
462 	}
463 	(void) dapl_os_memzero(&lifr, sizeof (lifr));
464 	(void) dapl_os_memcpy(&lifr.lifr_nd.lnr_addr, addr, sizeof (*addr));
465 	(void) dapl_os_strcpy(lifr.lifr_name, ia_ptr->hca_ptr->name);
466 
467 again:;
468 	if (ioctl(s, SIOCLIFGETND, (caddr_t)&lifr) < 0)  {
469 		/*
470 		 * if SIOCLIFGETND failed, we force the ND
471 		 * cache to be filled by connecting to the
472 		 * destination IP address.
473 		 */
474 		if (retries < NS_MAX_RETRIES &&
475 		    dapls_ns_send_packet_v6(s, addr) == 0 &&
476 		    dapls_ns_resolve_addr(AF_INET6, (struct sockaddr *)addr,
477 		    timeout) == 0) {
478 			retries++;
479 			goto again;
480 		}
481 		dapl_dbg_log(DAPL_DBG_TYPE_ERR, "ns_lookup_v6: giving up\n");
482 		(void) close(s);
483 		return (DAT_ERROR(DAT_INVALID_ADDRESS,
484 		    DAT_INVALID_ADDRESS_UNREACHABLE));
485 	}
486 	if (lifr.lifr_nd.lnr_hdw_len == 0 && retries <= NS_MAX_RETRIES) {
487 		/*
488 		 * lnr_hdw_len == 0 means that the ND entry
489 		 * is still incomplete. we need to retry the ioctl.
490 		 */
491 		retries++;
492 		(void) sleep(1);
493 		goto again;
494 	}
495 	(void) close(s);
496 
497 	mac = (uchar_t *)lifr.lifr_nd.lnr_hdw_addr;
498 	if (lifr.lifr_nd.lnr_hdw_len >= sizeof (ipoib_mac_t)) {
499 		ib_gid_t tmp_gid;
500 		/* LINTED: E_BAD_PTR_CAST_ALIGN */
501 		(void) dapl_os_memcpy(&tmp_gid,
502 		    &((ipoib_mac_t *)mac)->ipoib_gidpref, sizeof (ib_gid_t));
503 		/*
504 		 * gids from the ND table are in network order, convert
505 		 * the gids from network order to host byte order
506 		 */
507 		gid->gid_prefix = BETOH_64(tmp_gid.gid_prefix);
508 		gid->gid_guid = BETOH_64(tmp_gid.gid_guid);
509 	} else {
510 		int i, len;
511 
512 		len = lifr.lifr_nd.lnr_hdw_len;
513 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
514 		    "ns_lookup_v6: failed, non IB address: "
515 		    "len = %d, addr = 0x", len);
516 		if (len > 0) {
517 			for (i = 0; i < len; i++) {
518 				dapl_dbg_log(DAPL_DBG_TYPE_ERR,
519 				    "%02x", (int)mac[i] & 0xff);
520 			}
521 		} else {
522 			dapl_dbg_log(DAPL_DBG_TYPE_ERR, "0");
523 		}
524 		dapl_dbg_log(DAPL_DBG_TYPE_ERR, "\n");
525 		return (DAT_INVALID_ADDRESS);
526 	}
527 	return (DAT_SUCCESS);
528 }
529 
530 static int
dapls_ns_send_packet_v6(int s,struct sockaddr_in6 * addr)531 dapls_ns_send_packet_v6(int s, struct sockaddr_in6 *addr)
532 {
533 	if (sendto(s, NULL, 0, MSG_DONTROUTE, (struct sockaddr *)addr,
534 	    sizeof (*addr)) < 0) {
535 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
536 		    "ns_send_packet_v6: failed: %s\n", strerror(errno));
537 		return (-1);
538 	}
539 	return (0);
540 }
541 
542 static int
dapls_ns_subnet_match_v4(int s,DAPL_IA * ia_ptr,struct sockaddr_in * addr)543 dapls_ns_subnet_match_v4(int s, DAPL_IA *ia_ptr, struct sockaddr_in *addr)
544 {
545 	struct lifreq		lifreq;
546 	int			retval;
547 	uint32_t		netmask, netaddr, netaddr_dest;
548 
549 	(void) dapl_os_strcpy(lifreq.lifr_name, ia_ptr->hca_ptr->name);
550 
551 	retval = ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifreq);
552 	if (retval < 0) {
553 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
554 		    "ns_subnet_match_v4: cannot get netmask: %s\n",
555 		    strerror(errno));
556 		return (-1);
557 	}
558 	netmask = ((struct sockaddr_in *)&lifreq.lifr_addr)->
559 	    sin_addr.s_addr;
560 
561 	/*
562 	 * we need to get the interface address here because the
563 	 * address in ia_ptr->hca_ptr->hca_address might not
564 	 * necessarily be an IPv4 address.
565 	 */
566 	retval = ioctl(s, SIOCGLIFADDR, (caddr_t)&lifreq);
567 	if (retval < 0) {
568 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
569 		    "ns_subnet_match_v4: cannot get local addr: %s\n",
570 		    strerror(errno));
571 		return (-1);
572 	}
573 	netaddr = ((struct sockaddr_in *)&lifreq.lifr_addr)->
574 	    sin_addr.s_addr & netmask;
575 	netaddr_dest = addr->sin_addr.s_addr & netmask;
576 
577 	if (netaddr != netaddr_dest) {
578 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
579 		    "ns_subnet_match_v4: netaddrs don't match: "
580 		    "local %x, remote %x\n", netaddr, netaddr_dest);
581 		return (-1);
582 	}
583 	return (0);
584 }
585 
586 static int
dapls_ns_subnet_match_v6(int s,DAPL_IA * ia_ptr,struct sockaddr_in6 * addr)587 dapls_ns_subnet_match_v6(int s, DAPL_IA *ia_ptr, struct sockaddr_in6 *addr)
588 {
589 	struct lifreq		lifreq;
590 	struct sockaddr_in6	netmask_sock;
591 	uchar_t			*netmask, *local_addr, *dest_addr;
592 	int			i, retval;
593 
594 	(void) dapl_os_strcpy(lifreq.lifr_name, ia_ptr->hca_ptr->name);
595 
596 	retval = ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifreq);
597 	if (retval < 0) {
598 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
599 		    "ns_subnet_match_v6: cannot get netmask: %s\n",
600 		    strerror(errno));
601 		return (-1);
602 	}
603 	(void) dapl_os_memcpy(&netmask_sock, &lifreq.lifr_addr,
604 	    sizeof (netmask_sock));
605 
606 	/*
607 	 * we need to get the interface address here because the
608 	 * address in ia_ptr->hca_ptr->hca_address might not
609 	 * necessarily be an IPv6 address.
610 	 */
611 	retval = ioctl(s, SIOCGLIFADDR, (caddr_t)&lifreq);
612 	if (retval < 0) {
613 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
614 		    "ns_subnet_match_v6: cannot get local addr: %s\n",
615 		    strerror(errno));
616 		return (-1);
617 	}
618 	netmask = (uchar_t *)&netmask_sock.sin6_addr;
619 	local_addr = (uchar_t *)&((struct sockaddr_in6 *)&lifreq.lifr_addr)->
620 	    sin6_addr;
621 	dest_addr = (uchar_t *)&addr->sin6_addr;
622 
623 	for (i = 0; i < sizeof (addr->sin6_addr); i++) {
624 		if (((local_addr[i] ^ dest_addr[i]) & netmask[i]) != 0) {
625 			dapl_dbg_log(DAPL_DBG_TYPE_ERR,
626 			    "ns_subnet_match_v6: subnets do not match\n");
627 			return (-1);
628 		}
629 	}
630 	return (0);
631 }
632 
633 static int
dapls_ns_resolve_addr(int af,struct sockaddr * addr,DAT_TIMEOUT timeout)634 dapls_ns_resolve_addr(int af, struct sockaddr *addr, DAT_TIMEOUT timeout)
635 {
636 	struct sockaddr_storage	sock;
637 	struct sockaddr_in	*v4dest;
638 	struct sockaddr_in6	*v6dest;
639 	struct pollfd		pollfd;
640 	int			fd, retval;
641 	int			tmo;
642 	int			ip_version;
643 
644 	if (af == AF_INET) {
645 		ip_version = 4;
646 	} else if (af == AF_INET6) {
647 		ip_version = 6;
648 	} else {
649 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
650 		    "ns_resolve_addr: invalid af %d\n", af);
651 		return (-1);
652 	}
653 	fd = socket(af, SOCK_STREAM, 0);
654 	if (fd < 0) {
655 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
656 		    "ns_resolve_addr: ipv%d, cannot create socket %s\n",
657 		    ip_version, strerror(errno));
658 		return (-1);
659 	}
660 
661 	/*
662 	 * set socket to non-blocking mode
663 	 */
664 	retval = fcntl(fd, F_SETFL, O_NONBLOCK);
665 	if (retval < 0) {
666 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
667 		    "ns_resolve_addr: ipv%d, fcntl failed: %s\n",
668 		    ip_version, strerror(errno));
669 		(void) close(fd);
670 		return (-1);
671 	}
672 
673 	/*
674 	 * connect to the discard port (9) at the dest IP
675 	 */
676 	(void) dapl_os_memzero(&sock, sizeof (sock));
677 	if (af == AF_INET) {
678 		v4dest = (struct sockaddr_in *)&sock;
679 		v4dest->sin_family = AF_INET;
680 		v4dest->sin_addr.s_addr =
681 		    /* LINTED: E_BAD_PTR_CAST_ALIGN */
682 		    ((struct sockaddr_in *)addr)->sin_addr.s_addr;
683 		v4dest->sin_port = htons(9);
684 
685 		retval = connect(fd, (struct sockaddr *)v4dest,
686 		    sizeof (struct sockaddr_in));
687 	} else {
688 		v6dest = (struct sockaddr_in6 *)&sock;
689 		v6dest->sin6_family = AF_INET6;
690 		/* LINTED: E_BAD_PTR_CAST_ALIGN */
691 		(void) dapl_os_memcpy(&v6dest->sin6_addr,
692 		    &((struct sockaddr_in6 *)addr)->sin6_addr,
693 		    sizeof (struct sockaddr_in6));
694 		v6dest->sin6_port = htons(9);
695 
696 		retval = connect(fd, (struct sockaddr *)v6dest,
697 		    sizeof (struct sockaddr_in6));
698 	}
699 
700 	/*
701 	 * we can return immediately if connect succeeds
702 	 */
703 	if (retval == 0) {
704 		(void) close(fd);
705 		return (0);
706 	}
707 	/*
708 	 * receiving a RST means that the arp/nd entry should
709 	 * already be resolved
710 	 */
711 	if (retval < 0 && errno == ECONNREFUSED) {
712 		errno = 0;
713 		(void) close(fd);
714 		return (0);
715 	}
716 
717 	/*
718 	 * for all other cases, we poll on the fd
719 	 */
720 	pollfd.fd = fd;
721 	pollfd.events = POLLIN | POLLOUT;
722 	pollfd.revents = 0;
723 
724 	if (timeout == DAT_TIMEOUT_INFINITE ||
725 	    timeout == 0) {
726 		/*
727 		 * -1 means infinite
728 		 */
729 		tmo = -1;
730 	} else {
731 		/*
732 		 * convert timeout from usecs to msecs
733 		 */
734 		tmo = timeout/1000;
735 	}
736 	retval = poll(&pollfd, 1, tmo);
737 	if (retval > 0) {
738 		int	so_error = 0, len = sizeof (so_error);
739 
740 		retval = getsockopt(fd, SOL_SOCKET, SO_ERROR,
741 		    &so_error, &len);
742 		if (retval == 0) {
743 			/*
744 			 * we only return 0 if so_error == 0 or
745 			 * so_error == ECONNREFUSED. for all other
746 			 * cases retval is non-zero.
747 			 */
748 			if (so_error != 0 && so_error != ECONNREFUSED) {
749 				retval = -1;
750 				errno = so_error;
751 				dapl_dbg_log(DAPL_DBG_TYPE_ERR,
752 				    "ns_resolve_addr: ipv%d, so_error: %s\n",
753 				    ip_version, strerror(errno));
754 			}
755 		} else {
756 			/*
757 			 * if retval != 0, it must be -1. and errno must
758 			 * have been set by getsockopt.
759 			 */
760 			dapl_dbg_log(DAPL_DBG_TYPE_ERR,
761 			    "ns_resolve_addr: ipv%d, getsockopt: %s\n",
762 			    ip_version, strerror(errno));
763 		}
764 	} else {
765 		if (retval == 0) {
766 			errno = ETIMEDOUT;
767 		}
768 		retval = -1;
769 		dapl_dbg_log(DAPL_DBG_TYPE_ERR,
770 		    "ns_resolve_addr: ipv%d, poll: %s\n",
771 		    ip_version, strerror(errno));
772 	}
773 	(void) close(fd);
774 	return (retval);
775 }
776