util.c revision 74462
174462Salfred/*
274462Salfred * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $
374462Salfred * $FreeBSD: head/usr.sbin/rpcbind/util.c 74462 2001-03-19 12:50:13Z alfred $
474462Salfred */
574462Salfred
674462Salfred/*-
774462Salfred * Copyright (c) 2000 The NetBSD Foundation, Inc.
874462Salfred * All rights reserved.
974462Salfred *
1074462Salfred * This code is derived from software contributed to The NetBSD Foundation
1174462Salfred * by Frank van der Linden.
1274462Salfred *
1374462Salfred * Redistribution and use in source and binary forms, with or without
1474462Salfred * modification, are permitted provided that the following conditions
1574462Salfred * are met:
1674462Salfred * 1. Redistributions of source code must retain the above copyright
1774462Salfred *    notice, this list of conditions and the following disclaimer.
1874462Salfred * 2. Redistributions in binary form must reproduce the above copyright
1974462Salfred *    notice, this list of conditions and the following disclaimer in the
2074462Salfred *    documentation and/or other materials provided with the distribution.
2174462Salfred * 3. All advertising materials mentioning features or use of this software
2274462Salfred *    must display the following acknowledgement:
2374462Salfred *	This product includes software developed by the NetBSD
2474462Salfred *	Foundation, Inc. and its contributors.
2574462Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its
2674462Salfred *    contributors may be used to endorse or promote products derived
2774462Salfred *    from this software without specific prior written permission.
2874462Salfred *
2974462Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
3074462Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
3174462Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3274462Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3374462Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3474462Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3574462Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3674462Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3774462Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3874462Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3974462Salfred * POSSIBILITY OF SUCH DAMAGE.
4074462Salfred */
4174462Salfred
4274462Salfred#include <sys/types.h>
4374462Salfred#include <sys/socket.h>
4474462Salfred#include <sys/queue.h>
4574462Salfred#include <net/if.h>
4674462Salfred#include <netinet/in.h>
4774462Salfred#include <ifaddrs.h>
4874462Salfred#include <sys/poll.h>
4974462Salfred#include <rpc/rpc.h>
5074462Salfred#include <errno.h>
5174462Salfred#include <stdlib.h>
5274462Salfred#include <string.h>
5374462Salfred#include <unistd.h>
5474462Salfred#include <netdb.h>
5574462Salfred#include <netconfig.h>
5674462Salfred#include <stdio.h>
5774462Salfred#include <arpa/inet.h>
5874462Salfred
5974462Salfred#include "rpcbind.h"
6074462Salfred
6174462Salfredstatic struct sockaddr_in *local_in4;
6274462Salfred#ifdef INET6
6374462Salfredstatic struct sockaddr_in6 *local_in6;
6474462Salfred#endif
6574462Salfred
6674462Salfredstatic int bitmaskcmp __P((void *, void *, void *, int));
6774462Salfred#ifdef INET6
6874462Salfredstatic void in6_fillscopeid __P((struct sockaddr_in6 *));
6974462Salfred#endif
7074462Salfred
7174462Salfred/*
7274462Salfred * For all bits set in "mask", compare the corresponding bits in
7374462Salfred * "dst" and "src", and see if they match.
7474462Salfred */
7574462Salfredstatic int
7674462Salfredbitmaskcmp(void *dst, void *src, void *mask, int bytelen)
7774462Salfred{
7874462Salfred	int i, j;
7974462Salfred	u_int8_t *p1 = dst, *p2 = src, *netmask = mask;
8074462Salfred	u_int8_t bitmask;
8174462Salfred
8274462Salfred	for (i = 0; i < bytelen; i++) {
8374462Salfred		for (j = 0; j < 8; j++) {
8474462Salfred			bitmask = 1 << j;
8574462Salfred			if (!(netmask[i] & bitmask))
8674462Salfred				continue;
8774462Salfred			if ((p1[i] & bitmask) != (p2[i] & bitmask))
8874462Salfred				return 1;
8974462Salfred		}
9074462Salfred	}
9174462Salfred
9274462Salfred	return 0;
9374462Salfred}
9474462Salfred
9574462Salfred/*
9674462Salfred * Taken from ifconfig.c
9774462Salfred */
9874462Salfred#ifdef INET6
9974462Salfredstatic void
10074462Salfredin6_fillscopeid(struct sockaddr_in6 *sin6)
10174462Salfred{
10274462Salfred        if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
10374462Salfred                sin6->sin6_scope_id =
10474462Salfred                        ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
10574462Salfred                sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
10674462Salfred        }
10774462Salfred}
10874462Salfred#endif
10974462Salfred
11074462Salfredchar *
11174462Salfredaddrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
11274462Salfred	  char *netid)
11374462Salfred{
11474462Salfred	struct ifaddrs *ifap, *ifp, *bestif;
11574462Salfred#ifdef INET6
11674462Salfred	struct sockaddr_in6 *servsin6, *sin6mask, *clntsin6, *ifsin6, *realsin6;
11774462Salfred	struct sockaddr_in6 *newsin6;
11874462Salfred#endif
11974462Salfred	struct sockaddr_in *servsin, *sinmask, *clntsin, *newsin, *ifsin;
12074462Salfred	struct netbuf *serv_nbp, *clnt_nbp = NULL, tbuf;
12174462Salfred	struct sockaddr *serv_sa;
12274462Salfred	struct sockaddr *clnt_sa;
12374462Salfred	struct sockaddr_storage ss;
12474462Salfred	struct netconfig *nconf;
12574462Salfred	struct sockaddr *clnt = caller->buf;
12674462Salfred	char *ret = NULL;
12774462Salfred
12874462Salfred#ifdef ND_DEBUG
12974462Salfred	if (debugging)
13074462Salfred		fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr,
13174462Salfred		    clnt_uaddr, netid);
13274462Salfred#endif
13374462Salfred	nconf = getnetconfigent(netid);
13474462Salfred	if (nconf == NULL)
13574462Salfred		return NULL;
13674462Salfred
13774462Salfred	/*
13874462Salfred	 * Local merge, just return a duplicate.
13974462Salfred	 */
14074462Salfred	if (clnt_uaddr != NULL && strncmp(clnt_uaddr, "0.0.0.0.", 8) == 0)
14174462Salfred		return strdup(clnt_uaddr);
14274462Salfred
14374462Salfred	serv_nbp = uaddr2taddr(nconf, serv_uaddr);
14474462Salfred	if (serv_nbp == NULL)
14574462Salfred		return NULL;
14674462Salfred
14774462Salfred	serv_sa = (struct sockaddr *)serv_nbp->buf;
14874462Salfred	if (clnt_uaddr != NULL) {
14974462Salfred		clnt_nbp = uaddr2taddr(nconf, clnt_uaddr);
15074462Salfred		clnt_sa = (struct sockaddr *)clnt_nbp->buf;
15174462Salfred	} else {
15274462Salfred		clnt_sa = (struct sockaddr *)
15374462Salfred		    malloc(sizeof (struct sockaddr_storage));
15474462Salfred		memcpy(clnt_sa, clnt, clnt->sa_len);
15574462Salfred	}
15674462Salfred
15774462Salfred	if (getifaddrs(&ifp) < 0)
15874462Salfred		return 0;
15974462Salfred
16074462Salfred	/*
16174462Salfred	 * Loop through all interfaces. For each interface, see if the
16274462Salfred	 * network portion of its address is equal to that of the client.
16374462Salfred	 * If so, we have found the interface that we want to use.
16474462Salfred	 */
16574462Salfred	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
16674462Salfred		if (ifap->ifa_addr->sa_family != clnt->sa_family ||
16774462Salfred		    !(ifap->ifa_flags & IFF_UP))
16874462Salfred			continue;
16974462Salfred
17074462Salfred		switch (clnt->sa_family) {
17174462Salfred		case AF_INET:
17274462Salfred			/*
17374462Salfred			 * realsin: address that recvfrom gave us.
17474462Salfred			 * ifsin: address of interface being examined.
17574462Salfred			 * clntsin: address that client want us to contact
17674462Salfred			 *           it on
17774462Salfred			 * servsin: local address of RPC service.
17874462Salfred			 * sinmask: netmask of this interface
17974462Salfred			 * newsin: initially a copy of clntsin, eventually
18074462Salfred			 *         the merged address
18174462Salfred			 */
18274462Salfred			servsin = (struct sockaddr_in *)serv_sa;
18374462Salfred			clntsin = (struct sockaddr_in *)clnt_sa;
18474462Salfred			sinmask = (struct sockaddr_in *)ifap->ifa_netmask;
18574462Salfred			newsin = (struct sockaddr_in *)&ss;
18674462Salfred			ifsin = (struct sockaddr_in *)ifap->ifa_addr;
18774462Salfred			if (!bitmaskcmp(&ifsin->sin_addr, &clntsin->sin_addr,
18874462Salfred			    &sinmask->sin_addr, sizeof (struct in_addr))) {
18974462Salfred				/*
19074462Salfred				 * Found it.
19174462Salfred				 */
19274462Salfred				memcpy(newsin, ifap->ifa_addr,
19374462Salfred				    clnt_sa->sa_len);
19474462Salfred				newsin->sin_port = servsin->sin_port;
19574462Salfred				tbuf.len = clnt_sa->sa_len;
19674462Salfred				tbuf.maxlen = sizeof (struct sockaddr_storage);
19774462Salfred				tbuf.buf = newsin;
19874462Salfred				goto found;
19974462Salfred			}
20074462Salfred			break;
20174462Salfred#ifdef INET6
20274462Salfred		case AF_INET6:
20374462Salfred			/*
20474462Salfred			 * realsin6: address that recvfrom gave us.
20574462Salfred			 * ifsin6: address of interface being examined.
20674462Salfred			 * clntsin6: address that client want us to contact
20774462Salfred			 *           it on
20874462Salfred			 * servsin6: local address of RPC service.
20974462Salfred			 * sin6mask: netmask of this interface
21074462Salfred			 * newsin6: initially a copy of clntsin, eventually
21174462Salfred			 *          the merged address
21274462Salfred			 *
21374462Salfred			 * For v6 link local addresses, if the client contacted
21474462Salfred			 * us via a link-local address, and wants us to reply
21574462Salfred			 * to one, use the scope id to see which one.
21674462Salfred			 */
21774462Salfred			realsin6 = (struct sockaddr_in6 *)clnt;
21874462Salfred			ifsin6 = (struct sockaddr_in6 *)ifap->ifa_addr;
21974462Salfred			in6_fillscopeid(ifsin6);
22074462Salfred			clntsin6 = (struct sockaddr_in6 *)clnt_sa;
22174462Salfred			servsin6 = (struct sockaddr_in6 *)serv_sa;
22274462Salfred			sin6mask = (struct sockaddr_in6 *)ifap->ifa_netmask;
22374462Salfred			newsin6 = (struct sockaddr_in6 *)&ss;
22474462Salfred			if (IN6_IS_ADDR_LINKLOCAL(&ifsin6->sin6_addr) &&
22574462Salfred			    IN6_IS_ADDR_LINKLOCAL(&realsin6->sin6_addr) &&
22674462Salfred			    IN6_IS_ADDR_LINKLOCAL(&clntsin6->sin6_addr)) {
22774462Salfred				if (ifsin6->sin6_scope_id !=
22874462Salfred				    realsin6->sin6_scope_id)
22974462Salfred					continue;
23074462Salfredmatch:
23174462Salfred				memcpy(newsin6, ifsin6, clnt_sa->sa_len);
23274462Salfred				newsin6->sin6_port = servsin6->sin6_port;
23374462Salfred				tbuf.maxlen = sizeof (struct sockaddr_storage);
23474462Salfred				tbuf.len = clnt_sa->sa_len;
23574462Salfred				tbuf.buf = newsin6;
23674462Salfred				goto found;
23774462Salfred			}
23874462Salfred			if (!bitmaskcmp(&ifsin6->sin6_addr,
23974462Salfred			    &clntsin6->sin6_addr, &sin6mask->sin6_addr,
24074462Salfred			    sizeof (struct in6_addr)))
24174462Salfred				goto match;
24274462Salfred			break;
24374462Salfred#endif
24474462Salfred		default:
24574462Salfred			goto freeit;
24674462Salfred		}
24774462Salfred	}
24874462Salfred	/*
24974462Salfred	 * Didn't find anything. Get the first possibly useful interface,
25074462Salfred	 * preferring "normal" interfaces to point-to-point and loopback
25174462Salfred	 * ones.
25274462Salfred	 */
25374462Salfred	bestif = NULL;
25474462Salfred	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
25574462Salfred		if (ifap->ifa_addr->sa_family != clnt->sa_family ||
25674462Salfred		    !(ifap->ifa_flags & IFF_UP))
25774462Salfred			continue;
25874462Salfred		if (!(ifap->ifa_flags & IFF_LOOPBACK) &&
25974462Salfred		    !(ifap->ifa_flags & IFF_POINTOPOINT)) {
26074462Salfred			bestif = ifap;
26174462Salfred			break;
26274462Salfred		}
26374462Salfred		if (bestif == NULL)
26474462Salfred			bestif = ifap;
26574462Salfred		else if ((bestif->ifa_flags & IFF_LOOPBACK) &&
26674462Salfred		    !(ifap->ifa_flags & IFF_LOOPBACK))
26774462Salfred			bestif = ifap;
26874462Salfred	}
26974462Salfred	ifap = bestif;
27074462Salfredfound:
27174462Salfred	if (ifap != NULL)
27274462Salfred		ret = taddr2uaddr(nconf, &tbuf);
27374462Salfredfreeit:
27474462Salfred	freenetconfigent(nconf);
27574462Salfred	free(serv_sa);
27674462Salfred	free(serv_nbp);
27774462Salfred	if (clnt_sa != NULL)
27874462Salfred		free(clnt_sa);
27974462Salfred	if (clnt_nbp != NULL)
28074462Salfred		free(clnt_nbp);
28174462Salfred	freeifaddrs(ifp);
28274462Salfred
28374462Salfred#ifdef ND_DEBUG
28474462Salfred	if (debugging)
28574462Salfred		fprintf(stderr, "addrmerge: returning %s\n", ret);
28674462Salfred#endif
28774462Salfred	return ret;
28874462Salfred}
28974462Salfred
29074462Salfredvoid
29174462Salfrednetwork_init()
29274462Salfred{
29374462Salfred#ifdef INET6
29474462Salfred	struct ifaddrs *ifap, *ifp;
29574462Salfred	struct ipv6_mreq mreq6;
29674462Salfred	int ifindex, s;
29774462Salfred#endif
29874462Salfred	int ecode;
29974462Salfred	struct addrinfo hints, *res;
30074462Salfred
30174462Salfred	memset(&hints, 0, sizeof hints);
30274462Salfred	hints.ai_family = AF_INET;
30374462Salfred	if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
30474462Salfred		if (debugging)
30574462Salfred			fprintf(stderr, "can't get local ip4 address: %s\n",
30674462Salfred			    gai_strerror(ecode));
30774462Salfred	} else {
30874462Salfred		local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4);
30974462Salfred		if (local_in4 == NULL) {
31074462Salfred			if (debugging)
31174462Salfred				fprintf(stderr, "can't alloc local ip4 addr\n");
31274462Salfred		}
31374462Salfred		memcpy(local_in4, res->ai_addr, sizeof *local_in4);
31474462Salfred	}
31574462Salfred
31674462Salfred#ifdef INET6
31774462Salfred	hints.ai_family = AF_INET6;
31874462Salfred	if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
31974462Salfred		if (debugging)
32074462Salfred			fprintf(stderr, "can't get local ip6 address: %s\n",
32174462Salfred			    gai_strerror(ecode));
32274462Salfred	} else {
32374462Salfred		local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6);
32474462Salfred		if (local_in6 == NULL) {
32574462Salfred			if (debugging)
32674462Salfred				fprintf(stderr, "can't alloc local ip6 addr\n");
32774462Salfred		}
32874462Salfred		memcpy(local_in6, res->ai_addr, sizeof *local_in6);
32974462Salfred	}
33074462Salfred
33174462Salfred	/*
33274462Salfred	 * Now join the RPC ipv6 multicast group on all interfaces.
33374462Salfred	 */
33474462Salfred	if (getifaddrs(&ifp) < 0)
33574462Salfred		return;
33674462Salfred
33774462Salfred	mreq6.ipv6mr_interface = 0;
33874462Salfred	inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr);
33974462Salfred
34074462Salfred	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
34174462Salfred
34274462Salfred	/*
34374462Salfred	 * Loop through all interfaces. For each interface, see if the
34474462Salfred	 * network portion of its address is equal to that of the client.
34574462Salfred	 * If so, we have found the interface that we want to use.
34674462Salfred	 */
34774462Salfred	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
34874462Salfred		if (ifap->ifa_addr->sa_family != AF_INET6 ||
34974462Salfred		    !(ifap->ifa_flags & IFF_MULTICAST))
35074462Salfred			continue;
35174462Salfred		ifindex = if_nametoindex(ifap->ifa_name);
35274462Salfred		if (ifindex == mreq6.ipv6mr_interface)
35374462Salfred			/*
35474462Salfred			 * Already did this one.
35574462Salfred			 */
35674462Salfred			continue;
35774462Salfred		mreq6.ipv6mr_interface = ifindex;
35874462Salfred		if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
35974462Salfred		    sizeof mreq6) < 0)
36074462Salfred			if (debugging)
36174462Salfred				perror("setsockopt v6 multicast");
36274462Salfred	}
36374462Salfred#endif
36474462Salfred
36574462Salfred	/* close(s); */
36674462Salfred}
36774462Salfred
36874462Salfredstruct sockaddr *
36974462Salfredlocal_sa(int af)
37074462Salfred{
37174462Salfred	switch (af) {
37274462Salfred	case AF_INET:
37374462Salfred		return (struct sockaddr *)local_in4;
37474462Salfred#ifdef INET6
37574462Salfred	case AF_INET6:
37674462Salfred		return (struct sockaddr *)local_in6;
37774462Salfred#endif
37874462Salfred	default:
37974462Salfred		return NULL;
38074462Salfred	}
38174462Salfred}
382