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