1112758Ssam/* 2112758Ssam * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $ 3112758Ssam * $FreeBSD$ 4112758Ssam */ 5112758Ssam 6112758Ssam/*- 7112758Ssam * Copyright (c) 2000 The NetBSD Foundation, Inc. 8112758Ssam * All rights reserved. 9112758Ssam * 10112758Ssam * This code is derived from software contributed to The NetBSD Foundation 11112758Ssam * by Frank van der Linden. 12112758Ssam * 13112758Ssam * Redistribution and use in source and binary forms, with or without 14112758Ssam * modification, are permitted provided that the following conditions 15112758Ssam * are met: 16112758Ssam * 1. Redistributions of source code must retain the above copyright 17112758Ssam * notice, this list of conditions and the following disclaimer. 18112758Ssam * 2. Redistributions in binary form must reproduce the above copyright 19112758Ssam * notice, this list of conditions and the following disclaimer in the 20112758Ssam * documentation and/or other materials provided with the distribution. 21112758Ssam * 22112758Ssam * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23112758Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24112758Ssam * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25112758Ssam * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26112758Ssam * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27112758Ssam * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28105197Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29105197Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30105197Ssam * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31105197Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32105197Ssam * POSSIBILITY OF SUCH DAMAGE. 33105197Ssam */ 34105197Ssam 35105197Ssam#include <sys/types.h> 36105197Ssam#include <sys/socket.h> 37295126Sglebius#include <sys/queue.h> 38105197Ssam#include <net/if.h> 39105197Ssam#include <netinet/in.h> 40105197Ssam#include <ifaddrs.h> 41195699Srwatson#include <sys/poll.h> 42105197Ssam#include <rpc/rpc.h> 43105197Ssam#include <errno.h> 44105197Ssam#include <stdlib.h> 45105197Ssam#include <string.h> 46108989Ssam#include <unistd.h> 47108989Ssam#include <netdb.h> 48105197Ssam#include <netconfig.h> 49105197Ssam#include <stdio.h> 50105197Ssam#include <arpa/inet.h> 51105197Ssam 52105197Ssam#include "rpcbind.h" 53105197Ssam 54105197Ssamstatic struct sockaddr_in *local_in4; 55105197Ssam#ifdef INET6 56105197Ssamstatic struct sockaddr_in6 *local_in6; 57105197Ssam#endif 58105197Ssam 59120585Ssamstatic int bitmaskcmp(struct sockaddr *, struct sockaddr *, struct sockaddr *); 60120585Ssam 61105197Ssam/* 62105197Ssam * For all bits set in "mask", compare the corresponding bits in 63105197Ssam * "dst" and "src", and see if they match. Returns 0 if the addresses 64105197Ssam * match. 65105197Ssam */ 66105197Ssamstatic int 67105197Ssambitmaskcmp(struct sockaddr *dst, struct sockaddr *src, struct sockaddr *mask) 68105197Ssam{ 69105197Ssam int i; 70199894Sbz u_int8_t *p1, *p2, *netmask; 71105197Ssam int bytelen; 72105197Ssam 73105197Ssam if (dst->sa_family != src->sa_family || 74105197Ssam dst->sa_family != mask->sa_family) 75105197Ssam return (1); 76187815Svanhu 77187815Svanhu switch (dst->sa_family) { 78105197Ssam case AF_INET: 79187815Svanhu p1 = (uint8_t*) &SA2SINADDR(dst); 80187815Svanhu p2 = (uint8_t*) &SA2SINADDR(src); 81187815Svanhu netmask = (uint8_t*) &SA2SINADDR(mask); 82187815Svanhu bytelen = sizeof(struct in_addr); 83187815Svanhu break; 84187815Svanhu#ifdef INET6 85187815Svanhu case AF_INET6: 86243882Sglebius p1 = (uint8_t*) &SA2SIN6ADDR(dst); 87187815Svanhu p2 = (uint8_t*) &SA2SIN6ADDR(src); 88187815Svanhu netmask = (uint8_t*) &SA2SIN6ADDR(mask); 89187815Svanhu bytelen = sizeof(struct in6_addr); 90243882Sglebius break; 91187815Svanhu#endif 92187815Svanhu default: 93187815Svanhu return (1); 94187815Svanhu } 95187815Svanhu 96187815Svanhu for (i = 0; i < bytelen; i++) 97187815Svanhu if ((p1[i] & netmask[i]) != (p2[i] & netmask[i])) 98187815Svanhu return (1); 99187815Svanhu return (0); 100187815Svanhu} 101187815Svanhu 102187815Svanhu/* 103187815Svanhu * Find a server address that can be used by `caller' to contact 104187815Svanhu * the local service specified by `serv_uaddr'. If `clnt_uaddr' is 105187815Svanhu * non-NULL, it is used instead of `caller' as a hint suggesting 106187815Svanhu * the best address (e.g. the `r_addr' field of an rpc, which 107105197Ssam * contains the rpcbind server address that the caller used). 108105197Ssam * 109105197Ssam * Returns the best server address as a malloc'd "universal address" 110187815Svanhu * string which should be freed by the caller. On error, returns NULL. 111187815Svanhu */ 112187815Svanhuchar * 113187815Svanhuaddrmerge(struct netbuf *caller, const char *serv_uaddr, const char *clnt_uaddr, 114187815Svanhu const char *netid) 115187815Svanhu{ 116243882Sglebius struct ifaddrs *ifap, *ifp = NULL, *bestif; 117187815Svanhu struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf; 118187815Svanhu struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa; 119187815Svanhu struct sockaddr_storage ss; 120187815Svanhu struct netconfig *nconf; 121187815Svanhu char *caller_uaddr = NULL; 122105197Ssam#ifdef ND_DEBUG 123187815Svanhu const char *hint_uaddr = NULL; 124187815Svanhu#endif 125187815Svanhu char *ret = NULL; 126187815Svanhu int bestif_goodness; 127187815Svanhu 128187815Svanhu#ifdef ND_DEBUG 129187815Svanhu if (debugging) 130187815Svanhu fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, 131187815Svanhu clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid); 132187815Svanhu#endif 133105197Ssam caller_sa = caller->buf; 134105197Ssam if ((nconf = rpcbind_get_conf(netid)) == NULL) 135105197Ssam goto freeit; 136252026Sae if ((caller_uaddr = taddr2uaddr(nconf, caller)) == NULL) 137105197Ssam goto freeit; 138105197Ssam 139105197Ssam /* 140105197Ssam * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its 141105197Ssam * address family is different from that of the caller. 142113075Sdes */ 143113075Sdes hint_sa = NULL; 144105197Ssam if (clnt_uaddr != NULL) { 145105197Ssam#ifdef ND_DEBUG 146105197Ssam hint_uaddr = clnt_uaddr; 147105197Ssam#endif 148105197Ssam if ((hint_nbp = uaddr2taddr(nconf, clnt_uaddr)) == NULL) 149105197Ssam goto freeit; 150105197Ssam hint_sa = hint_nbp->buf; 151105197Ssam } 152105197Ssam if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) { 153105197Ssam#ifdef ND_DEBUG 154105197Ssam hint_uaddr = caller_uaddr; 155105197Ssam#endif 156105197Ssam hint_sa = caller->buf; 157105197Ssam } 158105197Ssam 159105197Ssam#ifdef ND_DEBUG 160105197Ssam if (debugging) 161105197Ssam fprintf(stderr, "addrmerge: hint %s\n", hint_uaddr); 162105197Ssam#endif 163105197Ssam /* Local caller, just return the server address. */ 164120585Ssam if (strncmp(caller_uaddr, "0.0.0.0.", 8) == 0 || 165105197Ssam strncmp(caller_uaddr, "::.", 3) == 0 || caller_uaddr[0] == '/') { 166105197Ssam ret = strdup(serv_uaddr); 167105197Ssam goto freeit; 168105197Ssam } 169105197Ssam 170105197Ssam if (getifaddrs(&ifp) < 0) 171105197Ssam goto freeit; 172105197Ssam 173105197Ssam /* 174105197Ssam * Loop through all interface addresses. We are listening to an address 175105197Ssam * if any of the following are true: 176105197Ssam * a) It's a loopback address 177105197Ssam * b) It was specified with the -h command line option 178105197Ssam * c) There were no -h command line options. 179120585Ssam * 180120585Ssam * Among addresses on which we are listening, choose in order of 181120585Ssam * preference an address that is: 182105197Ssam * 183105197Ssam * a) Equal to the hint 184105197Ssam * b) A link local address with the same scope ID as the client's 185105197Ssam * address, if the client's address is also link local 186105197Ssam * c) An address on the same subnet as the client's address 187105197Ssam * d) A non-localhost, non-p2p address 188105197Ssam * e) Any usable address 189105197Ssam */ 190120585Ssam bestif = NULL; 191120585Ssam bestif_goodness = 0; 192120585Ssam for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 193120585Ssam ifsa = ifap->ifa_addr; 194105197Ssam ifmasksa = ifap->ifa_netmask; 195105197Ssam 196105197Ssam /* Skip addresses where we don't listen */ 197105197Ssam if (ifsa == NULL || ifsa->sa_family != hint_sa->sa_family || 198105197Ssam !(ifap->ifa_flags & IFF_UP)) 199105197Ssam continue; 200105197Ssam 201105197Ssam if (!(ifap->ifa_flags & IFF_LOOPBACK) && !listen_addr(ifsa)) 202105197Ssam continue; 203105197Ssam 204243882Sglebius if ((hint_sa->sa_family == AF_INET) && 205298075Spfg ((((struct sockaddr_in*)hint_sa)->sin_addr.s_addr == 206105197Ssam ((struct sockaddr_in*)ifsa)->sin_addr.s_addr))) { 207120585Ssam const int goodness = 4; 208105197Ssam 209105197Ssam bestif_goodness = goodness; 210105197Ssam bestif = ifap; 211105197Ssam goto found; 212105197Ssam } 213105197Ssam#ifdef INET6 214105197Ssam if ((hint_sa->sa_family == AF_INET6) && 215105197Ssam (0 == memcmp(&((struct sockaddr_in6*)hint_sa)->sin6_addr, 216105197Ssam &((struct sockaddr_in6*)ifsa)->sin6_addr, 217105197Ssam sizeof(struct in6_addr))) && 218105197Ssam (((struct sockaddr_in6*)hint_sa)->sin6_scope_id == 219105197Ssam (((struct sockaddr_in6*)ifsa)->sin6_scope_id))) { 220105197Ssam const int goodness = 4; 221105197Ssam 222105197Ssam bestif_goodness = goodness; 223105197Ssam bestif = ifap; 224105197Ssam goto found; 225105197Ssam } 226105197Ssam if (hint_sa->sa_family == AF_INET6) { 227105197Ssam /* 228105197Ssam * For v6 link local addresses, if the caller is on 229105197Ssam * a link-local address then use the scope id to see 230105197Ssam * which one. 231105197Ssam */ 232105197Ssam if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa)) && 233105197Ssam IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) && 234105197Ssam IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa))) { 235105197Ssam if (SA2SIN6(ifsa)->sin6_scope_id == 236105197Ssam SA2SIN6(caller_sa)->sin6_scope_id) { 237105197Ssam const int goodness = 3; 238105197Ssam 239105197Ssam if (bestif_goodness < goodness) { 240105197Ssam bestif = ifap; 241105197Ssam bestif_goodness = goodness; 242252026Sae } 243105197Ssam } 244105197Ssam } 245105197Ssam } 246105197Ssam#endif /* INET6 */ 247105197Ssam if (0 == bitmaskcmp(hint_sa, ifsa, ifmasksa)) { 248105197Ssam const int goodness = 2; 249105197Ssam 250105197Ssam if (bestif_goodness < goodness) { 251105197Ssam bestif = ifap; 252105197Ssam bestif_goodness = goodness; 253105197Ssam } 254252026Sae } 255105197Ssam if (!(ifap->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { 256105197Ssam const int goodness = 1; 257105197Ssam 258105197Ssam if (bestif_goodness < goodness) { 259105197Ssam bestif = ifap; 260105197Ssam bestif_goodness = goodness; 261105197Ssam } 262105197Ssam } 263105197Ssam if (bestif == NULL) 264105197Ssam bestif = ifap; 265105197Ssam } 266105197Ssam if (bestif == NULL) 267105197Ssam goto freeit; 268105197Ssam 269105197Ssamfound: 270105197Ssam /* 271105197Ssam * Construct the new address using the address from 272105197Ssam * `bestif', and the port number from `serv_uaddr'. 273105197Ssam */ 274105197Ssam serv_nbp = uaddr2taddr(nconf, serv_uaddr); 275105197Ssam if (serv_nbp == NULL) 276105197Ssam goto freeit; 277105197Ssam serv_sa = serv_nbp->buf; 278105197Ssam 279252026Sae memcpy(&ss, bestif->ifa_addr, bestif->ifa_addr->sa_len); 280105197Ssam switch (ss.ss_family) { 281105197Ssam case AF_INET: 282105197Ssam SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port; 283105197Ssam break; 284105197Ssam#ifdef INET6 285105197Ssam case AF_INET6: 286105197Ssam SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port; 287105197Ssam break; 288105197Ssam#endif 289105197Ssam } 290105197Ssam tbuf.len = ss.ss_len; 291105197Ssam tbuf.maxlen = sizeof(ss); 292105197Ssam tbuf.buf = &ss; 293105197Ssam ret = taddr2uaddr(nconf, &tbuf); 294105197Ssam 295105197Ssamfreeit: 296105197Ssam free(caller_uaddr); 297105197Ssam if (hint_nbp != NULL) { 298105197Ssam free(hint_nbp->buf); 299105197Ssam free(hint_nbp); 300105197Ssam } 301105197Ssam if (serv_nbp != NULL) { 302105197Ssam free(serv_nbp->buf); 303105197Ssam free(serv_nbp); 304105197Ssam } 305105197Ssam if (ifp != NULL) 306105197Ssam freeifaddrs(ifp); 307105197Ssam 308105197Ssam#ifdef ND_DEBUG 309105197Ssam if (debugging) 310105197Ssam fprintf(stderr, "addrmerge: returning %s\n", ret); 311105197Ssam#endif 312105197Ssam return ret; 313105197Ssam} 314105197Ssam 315105197Ssamvoid 316105197Ssamnetwork_init(void) 317105197Ssam{ 318105197Ssam#ifdef INET6 319105197Ssam struct ifaddrs *ifap, *ifp; 320105197Ssam struct ipv6_mreq mreq6; 321105197Ssam unsigned int ifindex; 322105197Ssam int s; 323105197Ssam#endif 324105197Ssam int ecode; 325105197Ssam struct addrinfo hints, *res; 326 327 memset(&hints, 0, sizeof hints); 328 hints.ai_family = AF_INET; 329 if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { 330 if (debugging) 331 fprintf(stderr, "can't get local ip4 address: %s\n", 332 gai_strerror(ecode)); 333 } else { 334 local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4); 335 if (local_in4 == NULL) { 336 if (debugging) 337 fprintf(stderr, "can't alloc local ip4 addr\n"); 338 exit(1); 339 } 340 memcpy(local_in4, res->ai_addr, sizeof *local_in4); 341 freeaddrinfo(res); 342 } 343 344#ifdef INET6 345 hints.ai_family = AF_INET6; 346 if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { 347 if (debugging) 348 fprintf(stderr, "can't get local ip6 address: %s\n", 349 gai_strerror(ecode)); 350 } else { 351 local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6); 352 if (local_in6 == NULL) { 353 if (debugging) 354 fprintf(stderr, "can't alloc local ip6 addr\n"); 355 exit(1); 356 } 357 memcpy(local_in6, res->ai_addr, sizeof *local_in6); 358 freeaddrinfo(res); 359 } 360 361 /* 362 * Now join the RPC ipv6 multicast group on all interfaces. 363 */ 364 if (getifaddrs(&ifp) < 0) 365 return; 366 367 mreq6.ipv6mr_interface = 0; 368 inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr); 369 370 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 371 if (s == -1) { 372 if (debugging) 373 fprintf(stderr, "couldn't create ip6 socket"); 374 goto done_inet6; 375 } 376 377 /* 378 * Loop through all interfaces. For each IPv6 multicast-capable 379 * interface, join the RPC multicast group on that interface. 380 */ 381 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 382 if (ifap->ifa_addr->sa_family != AF_INET6 || 383 !(ifap->ifa_flags & IFF_MULTICAST)) 384 continue; 385 ifindex = if_nametoindex(ifap->ifa_name); 386 if (ifindex == mreq6.ipv6mr_interface) 387 /* 388 * Already did this one. 389 */ 390 continue; 391 mreq6.ipv6mr_interface = ifindex; 392 if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, 393 sizeof mreq6) < 0) 394 if (debugging) 395 perror("setsockopt v6 multicast"); 396 } 397done_inet6: 398 freeifaddrs(ifp); 399#endif 400 401 /* close(s); */ 402} 403 404struct sockaddr * 405local_sa(int af) 406{ 407 switch (af) { 408 case AF_INET: 409 return (struct sockaddr *)local_in4; 410#ifdef INET6 411 case AF_INET6: 412 return (struct sockaddr *)local_in6; 413#endif 414 default: 415 return NULL; 416 } 417} 418