xref: /dflybsd-src/usr.sbin/rpcbind/util.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /*-
286d7f5d3SJohn Marino  * Copyright (c) 2000 The NetBSD Foundation, Inc.
386d7f5d3SJohn Marino  * All rights reserved.
486d7f5d3SJohn Marino  *
586d7f5d3SJohn Marino  * This code is derived from software contributed to The NetBSD Foundation
686d7f5d3SJohn Marino  * by Frank van der Linden.
786d7f5d3SJohn Marino  *
886d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
986d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
1086d7f5d3SJohn Marino  * are met:
1186d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
1286d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
1386d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
1486d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
1586d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
1686d7f5d3SJohn Marino  * 3. All advertising materials mentioning features or use of this software
1786d7f5d3SJohn Marino  *    must display the following acknowledgement:
1886d7f5d3SJohn Marino  *	This product includes software developed by the NetBSD
1986d7f5d3SJohn Marino  *	Foundation, Inc. and its contributors.
2086d7f5d3SJohn Marino  * 4. Neither the name of The NetBSD Foundation nor the names of its
2186d7f5d3SJohn Marino  *    contributors may be used to endorse or promote products derived
2286d7f5d3SJohn Marino  *    from this software without specific prior written permission.
2386d7f5d3SJohn Marino  *
2486d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2586d7f5d3SJohn Marino  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2686d7f5d3SJohn Marino  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2786d7f5d3SJohn Marino  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2886d7f5d3SJohn Marino  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2986d7f5d3SJohn Marino  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3086d7f5d3SJohn Marino  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3186d7f5d3SJohn Marino  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3286d7f5d3SJohn Marino  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3386d7f5d3SJohn Marino  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3486d7f5d3SJohn Marino  * POSSIBILITY OF SUCH DAMAGE.
3586d7f5d3SJohn Marino  *
3686d7f5d3SJohn Marino  * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $
3786d7f5d3SJohn Marino  * $FreeBSD: src/usr.sbin/rpcbind/util.c,v 1.6 2007/11/07 10:53:39 kevlo Exp $
3886d7f5d3SJohn Marino  */
3986d7f5d3SJohn Marino 
4086d7f5d3SJohn Marino #include <sys/types.h>
4186d7f5d3SJohn Marino #include <sys/socket.h>
4286d7f5d3SJohn Marino #include <sys/queue.h>
4386d7f5d3SJohn Marino #include <net/if.h>
4486d7f5d3SJohn Marino #include <netinet/in.h>
4586d7f5d3SJohn Marino #include <ifaddrs.h>
4686d7f5d3SJohn Marino #include <sys/poll.h>
4786d7f5d3SJohn Marino #include <rpc/rpc.h>
4886d7f5d3SJohn Marino #include <errno.h>
4986d7f5d3SJohn Marino #include <stdlib.h>
5086d7f5d3SJohn Marino #include <string.h>
5186d7f5d3SJohn Marino #include <unistd.h>
5286d7f5d3SJohn Marino #include <netdb.h>
5386d7f5d3SJohn Marino #include <netconfig.h>
5486d7f5d3SJohn Marino #include <stdio.h>
5586d7f5d3SJohn Marino #include <arpa/inet.h>
5686d7f5d3SJohn Marino 
5786d7f5d3SJohn Marino #include "rpcbind.h"
5886d7f5d3SJohn Marino 
5986d7f5d3SJohn Marino #define	SA2SIN(sa)	((struct sockaddr_in *)(sa))
6086d7f5d3SJohn Marino #define	SA2SINADDR(sa)	(SA2SIN(sa)->sin_addr)
6186d7f5d3SJohn Marino #ifdef INET6
6286d7f5d3SJohn Marino #define	SA2SIN6(sa)	((struct sockaddr_in6 *)(sa))
6386d7f5d3SJohn Marino #define	SA2SIN6ADDR(sa)	(SA2SIN6(sa)->sin6_addr)
6486d7f5d3SJohn Marino #endif
6586d7f5d3SJohn Marino 
6686d7f5d3SJohn Marino static struct sockaddr_in *local_in4;
6786d7f5d3SJohn Marino #ifdef INET6
6886d7f5d3SJohn Marino static struct sockaddr_in6 *local_in6;
6986d7f5d3SJohn Marino #endif
7086d7f5d3SJohn Marino 
7186d7f5d3SJohn Marino static int bitmaskcmp(void *, void *, void *, int);
7286d7f5d3SJohn Marino #ifdef INET6
7386d7f5d3SJohn Marino static void in6_fillscopeid(struct sockaddr_in6 *);
7486d7f5d3SJohn Marino #endif
7586d7f5d3SJohn Marino 
7686d7f5d3SJohn Marino /*
7786d7f5d3SJohn Marino  * For all bits set in "mask", compare the corresponding bits in
7886d7f5d3SJohn Marino  * "dst" and "src", and see if they match. Returns 0 if the addresses
7986d7f5d3SJohn Marino  * match.
8086d7f5d3SJohn Marino  */
8186d7f5d3SJohn Marino static int
bitmaskcmp(void * dst,void * src,void * mask,int bytelen)8286d7f5d3SJohn Marino bitmaskcmp(void *dst, void *src, void *mask, int bytelen)
8386d7f5d3SJohn Marino {
8486d7f5d3SJohn Marino 	int i;
8586d7f5d3SJohn Marino 	u_int8_t *p1 = dst, *p2 = src, *netmask = mask;
8686d7f5d3SJohn Marino 
8786d7f5d3SJohn Marino 	for (i = 0; i < bytelen; i++)
8886d7f5d3SJohn Marino 		if ((p1[i] & netmask[i]) != (p2[i] & netmask[i]))
8986d7f5d3SJohn Marino 			return (1);
9086d7f5d3SJohn Marino 	return (0);
9186d7f5d3SJohn Marino }
9286d7f5d3SJohn Marino 
9386d7f5d3SJohn Marino /*
9486d7f5d3SJohn Marino  * Similar to code in ifconfig.c. Fill in the scope ID for link-local
9586d7f5d3SJohn Marino  * addresses returned by getifaddrs().
9686d7f5d3SJohn Marino  */
9786d7f5d3SJohn Marino #ifdef INET6
9886d7f5d3SJohn Marino static void
in6_fillscopeid(struct sockaddr_in6 * sin6)9986d7f5d3SJohn Marino in6_fillscopeid(struct sockaddr_in6 *sin6)
10086d7f5d3SJohn Marino {
10186d7f5d3SJohn Marino 	u_int16_t ifindex;
10286d7f5d3SJohn Marino 
10386d7f5d3SJohn Marino 	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
10486d7f5d3SJohn Marino 		ifindex = ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
10586d7f5d3SJohn Marino 		if (sin6->sin6_scope_id == 0 && ifindex != 0) {
10686d7f5d3SJohn Marino 			sin6->sin6_scope_id = ifindex;
10786d7f5d3SJohn Marino 			*(u_int16_t *)&sin6->sin6_addr.s6_addr[2] = 0;
10886d7f5d3SJohn Marino 		}
10986d7f5d3SJohn Marino 	}
11086d7f5d3SJohn Marino }
11186d7f5d3SJohn Marino #endif
11286d7f5d3SJohn Marino 
11386d7f5d3SJohn Marino /*
11486d7f5d3SJohn Marino  * Find a server address that can be used by `caller' to contact
11586d7f5d3SJohn Marino  * the local service specified by `serv_uaddr'. If `clnt_uaddr' is
11686d7f5d3SJohn Marino  * non-NULL, it is used instead of `caller' as a hint suggesting
11786d7f5d3SJohn Marino  * the best address (e.g. the `r_addr' field of an rpc, which
11886d7f5d3SJohn Marino  * contains the rpcbind server address that the caller used).
11986d7f5d3SJohn Marino  *
12086d7f5d3SJohn Marino  * Returns the best server address as a malloc'd "universal address"
12186d7f5d3SJohn Marino  * string which should be freed by the caller. On error, returns NULL.
12286d7f5d3SJohn Marino  */
12386d7f5d3SJohn Marino char *
addrmerge(struct netbuf * caller,char * serv_uaddr,char * clnt_uaddr,char * netid)12486d7f5d3SJohn Marino addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
12586d7f5d3SJohn Marino 	  char *netid)
12686d7f5d3SJohn Marino {
12786d7f5d3SJohn Marino 	struct ifaddrs *ifap, *ifp = NULL, *bestif;
12886d7f5d3SJohn Marino 	struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf;
12986d7f5d3SJohn Marino 	struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa;
13086d7f5d3SJohn Marino 	struct sockaddr_storage ss;
13186d7f5d3SJohn Marino 	struct netconfig *nconf;
13286d7f5d3SJohn Marino 	char *caller_uaddr = NULL, *hint_uaddr = NULL;
13386d7f5d3SJohn Marino 	char *ret = NULL;
13486d7f5d3SJohn Marino 
13586d7f5d3SJohn Marino #ifdef ND_DEBUG
13686d7f5d3SJohn Marino 	if (debugging)
13786d7f5d3SJohn Marino 		fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr,
13886d7f5d3SJohn Marino 		    clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid);
13986d7f5d3SJohn Marino #endif
14086d7f5d3SJohn Marino 	caller_sa = caller->buf;
14186d7f5d3SJohn Marino 	if ((nconf = rpcbind_get_conf(netid)) == NULL)
14286d7f5d3SJohn Marino 		goto freeit;
14386d7f5d3SJohn Marino 	if ((caller_uaddr = taddr2uaddr(nconf, caller)) == NULL)
14486d7f5d3SJohn Marino 		goto freeit;
14586d7f5d3SJohn Marino 
14686d7f5d3SJohn Marino 	/*
14786d7f5d3SJohn Marino 	 * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its
14886d7f5d3SJohn Marino 	 * address family is different from that of the caller.
14986d7f5d3SJohn Marino 	 */
15086d7f5d3SJohn Marino 	hint_sa = NULL;
15186d7f5d3SJohn Marino 	if (clnt_uaddr != NULL) {
15286d7f5d3SJohn Marino 		hint_uaddr = clnt_uaddr;
15386d7f5d3SJohn Marino 		if ((hint_nbp = uaddr2taddr(nconf, clnt_uaddr)) == NULL)
15486d7f5d3SJohn Marino 			goto freeit;
15586d7f5d3SJohn Marino 		hint_sa = hint_nbp->buf;
15686d7f5d3SJohn Marino 	}
15786d7f5d3SJohn Marino 	if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) {
15886d7f5d3SJohn Marino 		hint_uaddr = caller_uaddr;
15986d7f5d3SJohn Marino 		hint_sa = caller->buf;
16086d7f5d3SJohn Marino 	}
16186d7f5d3SJohn Marino 
16286d7f5d3SJohn Marino #ifdef ND_DEBUG
16386d7f5d3SJohn Marino 	if (debugging)
16486d7f5d3SJohn Marino 		fprintf(stderr, "addrmerge: hint %s\n", hint_uaddr);
16586d7f5d3SJohn Marino #endif
16686d7f5d3SJohn Marino 	/* Local caller, just return the server address. */
16786d7f5d3SJohn Marino 	if (strncmp(caller_uaddr, "0.0.0.0.", 8) == 0 ||
16886d7f5d3SJohn Marino 	    strncmp(caller_uaddr, "::.", 3) == 0 || caller_uaddr[0] == '/') {
16986d7f5d3SJohn Marino 		ret = strdup(serv_uaddr);
17086d7f5d3SJohn Marino 		goto freeit;
17186d7f5d3SJohn Marino 	}
17286d7f5d3SJohn Marino 
17386d7f5d3SJohn Marino 	if (getifaddrs(&ifp) < 0)
17486d7f5d3SJohn Marino 		goto freeit;
17586d7f5d3SJohn Marino 
17686d7f5d3SJohn Marino 	/*
17786d7f5d3SJohn Marino 	 * Loop through all interfaces. For each interface, see if the
17886d7f5d3SJohn Marino 	 * network portion of its address is equal to that of the client.
17986d7f5d3SJohn Marino 	 * If so, we have found the interface that we want to use.
18086d7f5d3SJohn Marino 	 */
18186d7f5d3SJohn Marino 	bestif = NULL;
18286d7f5d3SJohn Marino 	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
18386d7f5d3SJohn Marino 		ifsa = ifap->ifa_addr;
18486d7f5d3SJohn Marino 		ifmasksa = ifap->ifa_netmask;
18586d7f5d3SJohn Marino 
18686d7f5d3SJohn Marino 		if (ifsa == NULL || ifsa->sa_family != hint_sa->sa_family ||
18786d7f5d3SJohn Marino 		    !(ifap->ifa_flags & IFF_UP))
18886d7f5d3SJohn Marino 			continue;
18986d7f5d3SJohn Marino 
19086d7f5d3SJohn Marino 		switch (hint_sa->sa_family) {
19186d7f5d3SJohn Marino 		case AF_INET:
19286d7f5d3SJohn Marino 			/*
19386d7f5d3SJohn Marino 			 * If the hint address matches this interface
19486d7f5d3SJohn Marino 			 * address/netmask, then we're done.
19586d7f5d3SJohn Marino 			 */
19686d7f5d3SJohn Marino 			if (!bitmaskcmp(&SA2SINADDR(ifsa),
19786d7f5d3SJohn Marino 			    &SA2SINADDR(hint_sa), &SA2SINADDR(ifmasksa),
19886d7f5d3SJohn Marino 			    sizeof(struct in_addr))) {
19986d7f5d3SJohn Marino 				bestif = ifap;
20086d7f5d3SJohn Marino 				goto found;
20186d7f5d3SJohn Marino 			}
20286d7f5d3SJohn Marino 			break;
20386d7f5d3SJohn Marino #ifdef INET6
20486d7f5d3SJohn Marino 		case AF_INET6:
20586d7f5d3SJohn Marino 			/*
20686d7f5d3SJohn Marino 			 * For v6 link local addresses, if the caller is on
20786d7f5d3SJohn Marino 			 * a link-local address then use the scope id to see
20886d7f5d3SJohn Marino 			 * which one.
20986d7f5d3SJohn Marino 			 */
21086d7f5d3SJohn Marino 			in6_fillscopeid(SA2SIN6(ifsa));
21186d7f5d3SJohn Marino 			if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa)) &&
21286d7f5d3SJohn Marino 			    IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) &&
21386d7f5d3SJohn Marino 			    IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa))) {
21486d7f5d3SJohn Marino 				if (SA2SIN6(ifsa)->sin6_scope_id ==
21586d7f5d3SJohn Marino 				    SA2SIN6(caller_sa)->sin6_scope_id) {
21686d7f5d3SJohn Marino 					bestif = ifap;
21786d7f5d3SJohn Marino 					goto found;
21886d7f5d3SJohn Marino 				}
21986d7f5d3SJohn Marino 			} else if (!bitmaskcmp(&SA2SIN6ADDR(ifsa),
22086d7f5d3SJohn Marino 			    &SA2SIN6ADDR(hint_sa), &SA2SIN6ADDR(ifmasksa),
22186d7f5d3SJohn Marino 			    sizeof(struct in6_addr))) {
22286d7f5d3SJohn Marino 				bestif = ifap;
22386d7f5d3SJohn Marino 				goto found;
22486d7f5d3SJohn Marino 			}
22586d7f5d3SJohn Marino 			break;
22686d7f5d3SJohn Marino #endif
22786d7f5d3SJohn Marino 		default:
22886d7f5d3SJohn Marino 			continue;
22986d7f5d3SJohn Marino 		}
23086d7f5d3SJohn Marino 
23186d7f5d3SJohn Marino 		/*
23286d7f5d3SJohn Marino 		 * Remember the first possibly useful interface, preferring
23386d7f5d3SJohn Marino 		 * "normal" to point-to-point and loopback ones.
23486d7f5d3SJohn Marino 		 */
23586d7f5d3SJohn Marino 		if (bestif == NULL ||
23686d7f5d3SJohn Marino 		    (!(ifap->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
23786d7f5d3SJohn Marino 		    (bestif->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))))
23886d7f5d3SJohn Marino 			bestif = ifap;
23986d7f5d3SJohn Marino 	}
24086d7f5d3SJohn Marino 	if (bestif == NULL)
24186d7f5d3SJohn Marino 		goto freeit;
24286d7f5d3SJohn Marino 
24386d7f5d3SJohn Marino found:
24486d7f5d3SJohn Marino 	/*
24586d7f5d3SJohn Marino 	 * Construct the new address using the the address from
24686d7f5d3SJohn Marino 	 * `bestif', and the port number from `serv_uaddr'.
24786d7f5d3SJohn Marino 	 */
24886d7f5d3SJohn Marino 	serv_nbp = uaddr2taddr(nconf, serv_uaddr);
24986d7f5d3SJohn Marino 	if (serv_nbp == NULL)
25086d7f5d3SJohn Marino 		goto freeit;
25186d7f5d3SJohn Marino 	serv_sa = serv_nbp->buf;
25286d7f5d3SJohn Marino 
25386d7f5d3SJohn Marino 	memcpy(&ss, bestif->ifa_addr, bestif->ifa_addr->sa_len);
25486d7f5d3SJohn Marino 	switch (ss.ss_family) {
25586d7f5d3SJohn Marino 	case AF_INET:
25686d7f5d3SJohn Marino 		SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port;
25786d7f5d3SJohn Marino 		break;
25886d7f5d3SJohn Marino #ifdef INET6
25986d7f5d3SJohn Marino 	case AF_INET6:
26086d7f5d3SJohn Marino 		SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port;
26186d7f5d3SJohn Marino 		break;
26286d7f5d3SJohn Marino #endif
26386d7f5d3SJohn Marino 	}
26486d7f5d3SJohn Marino 	tbuf.len = ss.ss_len;
26586d7f5d3SJohn Marino 	tbuf.maxlen = sizeof(ss);
26686d7f5d3SJohn Marino 	tbuf.buf = &ss;
26786d7f5d3SJohn Marino 	ret = taddr2uaddr(nconf, &tbuf);
26886d7f5d3SJohn Marino 
26986d7f5d3SJohn Marino freeit:
27086d7f5d3SJohn Marino 	if (caller_uaddr != NULL)
27186d7f5d3SJohn Marino 		free(caller_uaddr);
27286d7f5d3SJohn Marino 	if (hint_nbp != NULL) {
27386d7f5d3SJohn Marino 		free(hint_nbp->buf);
27486d7f5d3SJohn Marino 		free(hint_nbp);
27586d7f5d3SJohn Marino 	}
27686d7f5d3SJohn Marino 	if (serv_nbp != NULL) {
27786d7f5d3SJohn Marino 		free(serv_nbp->buf);
27886d7f5d3SJohn Marino 		free(serv_nbp);
27986d7f5d3SJohn Marino 	}
28086d7f5d3SJohn Marino 	if (ifp != NULL)
28186d7f5d3SJohn Marino 		freeifaddrs(ifp);
28286d7f5d3SJohn Marino 
28386d7f5d3SJohn Marino #ifdef ND_DEBUG
28486d7f5d3SJohn Marino 	if (debugging)
28586d7f5d3SJohn Marino 		fprintf(stderr, "addrmerge: returning %s\n", ret);
28686d7f5d3SJohn Marino #endif
28786d7f5d3SJohn Marino 	return ret;
28886d7f5d3SJohn Marino }
28986d7f5d3SJohn Marino 
29086d7f5d3SJohn Marino void
network_init(void)29186d7f5d3SJohn Marino network_init(void)
29286d7f5d3SJohn Marino {
29386d7f5d3SJohn Marino #ifdef INET6
29486d7f5d3SJohn Marino 	struct ifaddrs *ifap, *ifp;
29586d7f5d3SJohn Marino 	struct ipv6_mreq mreq6;
29686d7f5d3SJohn Marino 	unsigned int ifindex;
29786d7f5d3SJohn Marino 	int s;
29886d7f5d3SJohn Marino #endif
29986d7f5d3SJohn Marino 	int ecode;
30086d7f5d3SJohn Marino 	struct addrinfo hints, *res;
30186d7f5d3SJohn Marino 
30286d7f5d3SJohn Marino 	memset(&hints, 0, sizeof hints);
30386d7f5d3SJohn Marino 	hints.ai_family = AF_INET;
30486d7f5d3SJohn Marino 	if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
30586d7f5d3SJohn Marino 		if (debugging)
30686d7f5d3SJohn Marino 			fprintf(stderr, "can't get local ip4 address: %s\n",
30786d7f5d3SJohn Marino 			    gai_strerror(ecode));
30886d7f5d3SJohn Marino 	} else {
30986d7f5d3SJohn Marino 		local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4);
31086d7f5d3SJohn Marino 		if (local_in4 == NULL) {
31186d7f5d3SJohn Marino 			if (debugging)
31286d7f5d3SJohn Marino 				fprintf(stderr, "can't alloc local ip4 addr\n");
31386d7f5d3SJohn Marino 		}
31486d7f5d3SJohn Marino 		memcpy(local_in4, res->ai_addr, sizeof *local_in4);
31586d7f5d3SJohn Marino 	}
31686d7f5d3SJohn Marino 
31786d7f5d3SJohn Marino #ifdef INET6
31886d7f5d3SJohn Marino 	hints.ai_family = AF_INET6;
31986d7f5d3SJohn Marino 	if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
32086d7f5d3SJohn Marino 		if (debugging)
32186d7f5d3SJohn Marino 			fprintf(stderr, "can't get local ip6 address: %s\n",
32286d7f5d3SJohn Marino 			    gai_strerror(ecode));
32386d7f5d3SJohn Marino 	} else {
32486d7f5d3SJohn Marino 		local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6);
32586d7f5d3SJohn Marino 		if (local_in6 == NULL) {
32686d7f5d3SJohn Marino 			if (debugging)
32786d7f5d3SJohn Marino 				fprintf(stderr, "can't alloc local ip6 addr\n");
32886d7f5d3SJohn Marino 		}
32986d7f5d3SJohn Marino 		memcpy(local_in6, res->ai_addr, sizeof *local_in6);
33086d7f5d3SJohn Marino 	}
33186d7f5d3SJohn Marino 
33286d7f5d3SJohn Marino 	/*
33386d7f5d3SJohn Marino 	 * Now join the RPC ipv6 multicast group on all interfaces.
33486d7f5d3SJohn Marino 	 */
33586d7f5d3SJohn Marino 	if (getifaddrs(&ifp) < 0)
33686d7f5d3SJohn Marino 		return;
33786d7f5d3SJohn Marino 
33886d7f5d3SJohn Marino 	mreq6.ipv6mr_interface = 0;
33986d7f5d3SJohn Marino 	inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr);
34086d7f5d3SJohn Marino 
34186d7f5d3SJohn Marino 	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
34286d7f5d3SJohn Marino 
34386d7f5d3SJohn Marino 	/*
34486d7f5d3SJohn Marino 	 * Loop through all interfaces. For each IPv6 multicast-capable
34586d7f5d3SJohn Marino 	 * interface, join the RPC multicast group on that interface.
34686d7f5d3SJohn Marino 	 */
34786d7f5d3SJohn Marino 	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
34886d7f5d3SJohn Marino 		if (ifap->ifa_addr->sa_family != AF_INET6 ||
34986d7f5d3SJohn Marino 		    !(ifap->ifa_flags & IFF_MULTICAST))
35086d7f5d3SJohn Marino 			continue;
35186d7f5d3SJohn Marino 		ifindex = if_nametoindex(ifap->ifa_name);
35286d7f5d3SJohn Marino 		if (ifindex == mreq6.ipv6mr_interface)
35386d7f5d3SJohn Marino 			/*
35486d7f5d3SJohn Marino 			 * Already did this one.
35586d7f5d3SJohn Marino 			 */
35686d7f5d3SJohn Marino 			continue;
35786d7f5d3SJohn Marino 		mreq6.ipv6mr_interface = ifindex;
35886d7f5d3SJohn Marino 		if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
35986d7f5d3SJohn Marino 		    sizeof mreq6) < 0)
36086d7f5d3SJohn Marino 			if (debugging)
36186d7f5d3SJohn Marino 				perror("setsockopt v6 multicast");
36286d7f5d3SJohn Marino 	}
36386d7f5d3SJohn Marino #endif
36486d7f5d3SJohn Marino 
36586d7f5d3SJohn Marino 	/* close(s); */
36686d7f5d3SJohn Marino }
36786d7f5d3SJohn Marino 
36886d7f5d3SJohn Marino struct sockaddr *
local_sa(int af)36986d7f5d3SJohn Marino local_sa(int af)
37086d7f5d3SJohn Marino {
37186d7f5d3SJohn Marino 	switch (af) {
37286d7f5d3SJohn Marino 	case AF_INET:
37386d7f5d3SJohn Marino 		return (struct sockaddr *)local_in4;
37486d7f5d3SJohn Marino #ifdef INET6
37586d7f5d3SJohn Marino 	case AF_INET6:
37686d7f5d3SJohn Marino 		return (struct sockaddr *)local_in6;
37786d7f5d3SJohn Marino #endif
37886d7f5d3SJohn Marino 	default:
37986d7f5d3SJohn Marino 		return NULL;
38086d7f5d3SJohn Marino 	}
38186d7f5d3SJohn Marino }
382