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