util.c revision 74462
1/* 2 * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $ 3 * $FreeBSD: head/usr.sbin/rpcbind/util.c 74462 2001-03-19 12:50:13Z alfred $ 4 */ 5 6/*- 7 * Copyright (c) 2000 The NetBSD Foundation, Inc. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to The NetBSD Foundation 11 * by Frank van der Linden. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the NetBSD 24 * Foundation, Inc. and its contributors. 25 * 4. Neither the name of The NetBSD Foundation nor the names of its 26 * contributors may be used to endorse or promote products derived 27 * from this software without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39 * POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42#include <sys/types.h> 43#include <sys/socket.h> 44#include <sys/queue.h> 45#include <net/if.h> 46#include <netinet/in.h> 47#include <ifaddrs.h> 48#include <sys/poll.h> 49#include <rpc/rpc.h> 50#include <errno.h> 51#include <stdlib.h> 52#include <string.h> 53#include <unistd.h> 54#include <netdb.h> 55#include <netconfig.h> 56#include <stdio.h> 57#include <arpa/inet.h> 58 59#include "rpcbind.h" 60 61static struct sockaddr_in *local_in4; 62#ifdef INET6 63static struct sockaddr_in6 *local_in6; 64#endif 65 66static int bitmaskcmp __P((void *, void *, void *, int)); 67#ifdef INET6 68static void in6_fillscopeid __P((struct sockaddr_in6 *)); 69#endif 70 71/* 72 * For all bits set in "mask", compare the corresponding bits in 73 * "dst" and "src", and see if they match. 74 */ 75static int 76bitmaskcmp(void *dst, void *src, void *mask, int bytelen) 77{ 78 int i, j; 79 u_int8_t *p1 = dst, *p2 = src, *netmask = mask; 80 u_int8_t bitmask; 81 82 for (i = 0; i < bytelen; i++) { 83 for (j = 0; j < 8; j++) { 84 bitmask = 1 << j; 85 if (!(netmask[i] & bitmask)) 86 continue; 87 if ((p1[i] & bitmask) != (p2[i] & bitmask)) 88 return 1; 89 } 90 } 91 92 return 0; 93} 94 95/* 96 * Taken from ifconfig.c 97 */ 98#ifdef INET6 99static void 100in6_fillscopeid(struct sockaddr_in6 *sin6) 101{ 102 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 103 sin6->sin6_scope_id = 104 ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); 105 sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; 106 } 107} 108#endif 109 110char * 111addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, 112 char *netid) 113{ 114 struct ifaddrs *ifap, *ifp, *bestif; 115#ifdef INET6 116 struct sockaddr_in6 *servsin6, *sin6mask, *clntsin6, *ifsin6, *realsin6; 117 struct sockaddr_in6 *newsin6; 118#endif 119 struct sockaddr_in *servsin, *sinmask, *clntsin, *newsin, *ifsin; 120 struct netbuf *serv_nbp, *clnt_nbp = NULL, tbuf; 121 struct sockaddr *serv_sa; 122 struct sockaddr *clnt_sa; 123 struct sockaddr_storage ss; 124 struct netconfig *nconf; 125 struct sockaddr *clnt = caller->buf; 126 char *ret = NULL; 127 128#ifdef ND_DEBUG 129 if (debugging) 130 fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, 131 clnt_uaddr, netid); 132#endif 133 nconf = getnetconfigent(netid); 134 if (nconf == NULL) 135 return NULL; 136 137 /* 138 * Local merge, just return a duplicate. 139 */ 140 if (clnt_uaddr != NULL && strncmp(clnt_uaddr, "0.0.0.0.", 8) == 0) 141 return strdup(clnt_uaddr); 142 143 serv_nbp = uaddr2taddr(nconf, serv_uaddr); 144 if (serv_nbp == NULL) 145 return NULL; 146 147 serv_sa = (struct sockaddr *)serv_nbp->buf; 148 if (clnt_uaddr != NULL) { 149 clnt_nbp = uaddr2taddr(nconf, clnt_uaddr); 150 clnt_sa = (struct sockaddr *)clnt_nbp->buf; 151 } else { 152 clnt_sa = (struct sockaddr *) 153 malloc(sizeof (struct sockaddr_storage)); 154 memcpy(clnt_sa, clnt, clnt->sa_len); 155 } 156 157 if (getifaddrs(&ifp) < 0) 158 return 0; 159 160 /* 161 * Loop through all interfaces. For each interface, see if the 162 * network portion of its address is equal to that of the client. 163 * If so, we have found the interface that we want to use. 164 */ 165 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 166 if (ifap->ifa_addr->sa_family != clnt->sa_family || 167 !(ifap->ifa_flags & IFF_UP)) 168 continue; 169 170 switch (clnt->sa_family) { 171 case AF_INET: 172 /* 173 * realsin: address that recvfrom gave us. 174 * ifsin: address of interface being examined. 175 * clntsin: address that client want us to contact 176 * it on 177 * servsin: local address of RPC service. 178 * sinmask: netmask of this interface 179 * newsin: initially a copy of clntsin, eventually 180 * the merged address 181 */ 182 servsin = (struct sockaddr_in *)serv_sa; 183 clntsin = (struct sockaddr_in *)clnt_sa; 184 sinmask = (struct sockaddr_in *)ifap->ifa_netmask; 185 newsin = (struct sockaddr_in *)&ss; 186 ifsin = (struct sockaddr_in *)ifap->ifa_addr; 187 if (!bitmaskcmp(&ifsin->sin_addr, &clntsin->sin_addr, 188 &sinmask->sin_addr, sizeof (struct in_addr))) { 189 /* 190 * Found it. 191 */ 192 memcpy(newsin, ifap->ifa_addr, 193 clnt_sa->sa_len); 194 newsin->sin_port = servsin->sin_port; 195 tbuf.len = clnt_sa->sa_len; 196 tbuf.maxlen = sizeof (struct sockaddr_storage); 197 tbuf.buf = newsin; 198 goto found; 199 } 200 break; 201#ifdef INET6 202 case AF_INET6: 203 /* 204 * realsin6: address that recvfrom gave us. 205 * ifsin6: address of interface being examined. 206 * clntsin6: address that client want us to contact 207 * it on 208 * servsin6: local address of RPC service. 209 * sin6mask: netmask of this interface 210 * newsin6: initially a copy of clntsin, eventually 211 * the merged address 212 * 213 * For v6 link local addresses, if the client contacted 214 * us via a link-local address, and wants us to reply 215 * to one, use the scope id to see which one. 216 */ 217 realsin6 = (struct sockaddr_in6 *)clnt; 218 ifsin6 = (struct sockaddr_in6 *)ifap->ifa_addr; 219 in6_fillscopeid(ifsin6); 220 clntsin6 = (struct sockaddr_in6 *)clnt_sa; 221 servsin6 = (struct sockaddr_in6 *)serv_sa; 222 sin6mask = (struct sockaddr_in6 *)ifap->ifa_netmask; 223 newsin6 = (struct sockaddr_in6 *)&ss; 224 if (IN6_IS_ADDR_LINKLOCAL(&ifsin6->sin6_addr) && 225 IN6_IS_ADDR_LINKLOCAL(&realsin6->sin6_addr) && 226 IN6_IS_ADDR_LINKLOCAL(&clntsin6->sin6_addr)) { 227 if (ifsin6->sin6_scope_id != 228 realsin6->sin6_scope_id) 229 continue; 230match: 231 memcpy(newsin6, ifsin6, clnt_sa->sa_len); 232 newsin6->sin6_port = servsin6->sin6_port; 233 tbuf.maxlen = sizeof (struct sockaddr_storage); 234 tbuf.len = clnt_sa->sa_len; 235 tbuf.buf = newsin6; 236 goto found; 237 } 238 if (!bitmaskcmp(&ifsin6->sin6_addr, 239 &clntsin6->sin6_addr, &sin6mask->sin6_addr, 240 sizeof (struct in6_addr))) 241 goto match; 242 break; 243#endif 244 default: 245 goto freeit; 246 } 247 } 248 /* 249 * Didn't find anything. Get the first possibly useful interface, 250 * preferring "normal" interfaces to point-to-point and loopback 251 * ones. 252 */ 253 bestif = NULL; 254 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 255 if (ifap->ifa_addr->sa_family != clnt->sa_family || 256 !(ifap->ifa_flags & IFF_UP)) 257 continue; 258 if (!(ifap->ifa_flags & IFF_LOOPBACK) && 259 !(ifap->ifa_flags & IFF_POINTOPOINT)) { 260 bestif = ifap; 261 break; 262 } 263 if (bestif == NULL) 264 bestif = ifap; 265 else if ((bestif->ifa_flags & IFF_LOOPBACK) && 266 !(ifap->ifa_flags & IFF_LOOPBACK)) 267 bestif = ifap; 268 } 269 ifap = bestif; 270found: 271 if (ifap != NULL) 272 ret = taddr2uaddr(nconf, &tbuf); 273freeit: 274 freenetconfigent(nconf); 275 free(serv_sa); 276 free(serv_nbp); 277 if (clnt_sa != NULL) 278 free(clnt_sa); 279 if (clnt_nbp != NULL) 280 free(clnt_nbp); 281 freeifaddrs(ifp); 282 283#ifdef ND_DEBUG 284 if (debugging) 285 fprintf(stderr, "addrmerge: returning %s\n", ret); 286#endif 287 return ret; 288} 289 290void 291network_init() 292{ 293#ifdef INET6 294 struct ifaddrs *ifap, *ifp; 295 struct ipv6_mreq mreq6; 296 int ifindex, s; 297#endif 298 int ecode; 299 struct addrinfo hints, *res; 300 301 memset(&hints, 0, sizeof hints); 302 hints.ai_family = AF_INET; 303 if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { 304 if (debugging) 305 fprintf(stderr, "can't get local ip4 address: %s\n", 306 gai_strerror(ecode)); 307 } else { 308 local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4); 309 if (local_in4 == NULL) { 310 if (debugging) 311 fprintf(stderr, "can't alloc local ip4 addr\n"); 312 } 313 memcpy(local_in4, res->ai_addr, sizeof *local_in4); 314 } 315 316#ifdef INET6 317 hints.ai_family = AF_INET6; 318 if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { 319 if (debugging) 320 fprintf(stderr, "can't get local ip6 address: %s\n", 321 gai_strerror(ecode)); 322 } else { 323 local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6); 324 if (local_in6 == NULL) { 325 if (debugging) 326 fprintf(stderr, "can't alloc local ip6 addr\n"); 327 } 328 memcpy(local_in6, res->ai_addr, sizeof *local_in6); 329 } 330 331 /* 332 * Now join the RPC ipv6 multicast group on all interfaces. 333 */ 334 if (getifaddrs(&ifp) < 0) 335 return; 336 337 mreq6.ipv6mr_interface = 0; 338 inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr); 339 340 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 341 342 /* 343 * Loop through all interfaces. For each interface, see if the 344 * network portion of its address is equal to that of the client. 345 * If so, we have found the interface that we want to use. 346 */ 347 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 348 if (ifap->ifa_addr->sa_family != AF_INET6 || 349 !(ifap->ifa_flags & IFF_MULTICAST)) 350 continue; 351 ifindex = if_nametoindex(ifap->ifa_name); 352 if (ifindex == mreq6.ipv6mr_interface) 353 /* 354 * Already did this one. 355 */ 356 continue; 357 mreq6.ipv6mr_interface = ifindex; 358 if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, 359 sizeof mreq6) < 0) 360 if (debugging) 361 perror("setsockopt v6 multicast"); 362 } 363#endif 364 365 /* close(s); */ 366} 367 368struct sockaddr * 369local_sa(int af) 370{ 371 switch (af) { 372 case AF_INET: 373 return (struct sockaddr *)local_in4; 374#ifdef INET6 375 case AF_INET6: 376 return (struct sockaddr *)local_in6; 377#endif 378 default: 379 return NULL; 380 } 381} 382