1 /* $NetBSD: util.c,v 1.17 2013/10/19 17:16:25 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 50 #include "rpcbind.h" 51 52 static struct sockaddr_in *local_in4; 53 #ifdef INET6 54 static struct sockaddr_in6 *local_in6; 55 #endif 56 57 static int bitmaskcmp(void *, void *, void *, int); 58 59 /* 60 * For all bits set in "mask", compare the corresponding bits in 61 * "dst" and "src", and see if they match. 62 */ 63 static int 64 bitmaskcmp(void *dst, void *src, void *mask, int bytelen) 65 { 66 int i, j; 67 u_int8_t *p1 = dst, *p2 = src, *netmask = mask; 68 u_int8_t bitmask; 69 70 for (i = 0; i < bytelen; i++) { 71 for (j = 0; j < 8; j++) { 72 bitmask = 1 << j; 73 if (!(netmask[i] & bitmask)) 74 continue; 75 if ((p1[i] & bitmask) != (p2[i] & bitmask)) 76 return 1; 77 } 78 } 79 80 return 0; 81 } 82 83 char * 84 addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, 85 char *netid) 86 { 87 struct ifaddrs *ifap, *ifp, *bestif; 88 #ifdef INET6 89 struct sockaddr_in6 *servsin6, *sin6mask, *clntsin6, *ifsin6, *realsin6; 90 struct sockaddr_in6 *newsin6; 91 #endif 92 struct sockaddr_in *servsin, *sinmask, *clntsin, *newsin, *ifsin; 93 struct netbuf *serv_nbp, *clnt_nbp = NULL, tbuf; 94 struct sockaddr *serv_sa; 95 struct sockaddr *clnt_sa; 96 struct sockaddr_storage ss; 97 struct netconfig *nconf; 98 struct sockaddr *clnt = caller->buf; 99 char *ret = NULL; 100 101 #ifdef INET6 102 servsin6 = ifsin6 = newsin6 = NULL; /* XXXGCC -Wuninitialized */ 103 #endif 104 servsin = newsin = NULL; /* XXXGCC -Wuninitialized */ 105 106 #ifdef RPCBIND_DEBUG 107 if (debugging) 108 fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, 109 clnt_uaddr, netid); 110 #endif 111 nconf = getnetconfigent(netid); 112 if (nconf == NULL) 113 return NULL; 114 115 /* 116 * Local merge, just return a duplicate. 117 */ 118 if (clnt_uaddr != NULL && strncmp(clnt_uaddr, "0.0.0.0.", 8) == 0) 119 return strdup(clnt_uaddr); 120 121 serv_nbp = uaddr2taddr(nconf, serv_uaddr); 122 if (serv_nbp == NULL) 123 return NULL; 124 125 serv_sa = (struct sockaddr *)serv_nbp->buf; 126 if (clnt_uaddr != NULL) { 127 clnt_nbp = uaddr2taddr(nconf, clnt_uaddr); 128 if (clnt_nbp == NULL) { 129 free(serv_nbp); 130 return NULL; 131 } 132 clnt_sa = (struct sockaddr *)clnt_nbp->buf; 133 if (clnt_sa->sa_family == AF_LOCAL) { 134 free(serv_nbp); 135 free(clnt_nbp); 136 free(clnt_sa); 137 return strdup(serv_uaddr); 138 } 139 } else { 140 clnt_sa = (struct sockaddr *) 141 malloc(sizeof (struct sockaddr_storage)); 142 memcpy(clnt_sa, clnt, clnt->sa_len); 143 } 144 145 if (getifaddrs(&ifp) < 0) { 146 free(serv_nbp); 147 free(clnt_sa); 148 if (clnt_nbp != NULL) 149 free(clnt_nbp); 150 return 0; 151 } 152 153 /* 154 * Loop through all interfaces. For each interface, see if the 155 * network portion of its address is equal to that of the client. 156 * If so, we have found the interface that we want to use. 157 */ 158 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 159 if (ifap->ifa_addr->sa_family != clnt->sa_family || 160 !(ifap->ifa_flags & IFF_UP)) 161 continue; 162 163 switch (clnt->sa_family) { 164 case AF_INET: 165 /* 166 * realsin: address that recvfrom gave us. 167 * ifsin: address of interface being examined. 168 * clntsin: address that client want us to contact 169 * it on 170 * servsin: local address of RPC service. 171 * sinmask: netmask of this interface 172 * newsin: initially a copy of clntsin, eventually 173 * the merged address 174 */ 175 servsin = (struct sockaddr_in *)serv_sa; 176 clntsin = (struct sockaddr_in *)clnt_sa; 177 sinmask = (struct sockaddr_in *)ifap->ifa_netmask; 178 newsin = (struct sockaddr_in *)&ss; 179 ifsin = (struct sockaddr_in *)ifap->ifa_addr; 180 if (!bitmaskcmp(&ifsin->sin_addr, &clntsin->sin_addr, 181 &sinmask->sin_addr, sizeof (struct in_addr))) { 182 goto found; 183 } 184 break; 185 #ifdef INET6 186 case AF_INET6: 187 /* 188 * realsin6: address that recvfrom gave us. 189 * ifsin6: address of interface being examined. 190 * clntsin6: address that client want us to contact 191 * it on 192 * servsin6: local address of RPC service. 193 * sin6mask: netmask of this interface 194 * newsin6: initially a copy of clntsin, eventually 195 * the merged address 196 * 197 * For v6 link local addresses, if the client contacted 198 * us via a link-local address, and wants us to reply 199 * to one, use the scope id to see which one. 200 */ 201 realsin6 = (struct sockaddr_in6 *)clnt; 202 ifsin6 = (struct sockaddr_in6 *)ifap->ifa_addr; 203 inet6_getscopeid(ifsin6, 1); 204 clntsin6 = (struct sockaddr_in6 *)clnt_sa; 205 servsin6 = (struct sockaddr_in6 *)serv_sa; 206 sin6mask = (struct sockaddr_in6 *)ifap->ifa_netmask; 207 newsin6 = (struct sockaddr_in6 *)&ss; 208 if (IN6_IS_ADDR_LINKLOCAL(&ifsin6->sin6_addr) && 209 IN6_IS_ADDR_LINKLOCAL(&realsin6->sin6_addr) && 210 IN6_IS_ADDR_LINKLOCAL(&clntsin6->sin6_addr)) { 211 if (ifsin6->sin6_scope_id != 212 realsin6->sin6_scope_id) 213 continue; 214 goto found; 215 } 216 if (!bitmaskcmp(&ifsin6->sin6_addr, 217 &clntsin6->sin6_addr, &sin6mask->sin6_addr, 218 sizeof (struct in6_addr))) 219 goto found; 220 break; 221 #endif 222 default: 223 goto freeit; 224 } 225 } 226 /* 227 * Didn't find anything. Get the first possibly useful interface, 228 * preferring "normal" interfaces to point-to-point and loopback 229 * ones. 230 */ 231 bestif = NULL; 232 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 233 if (ifap->ifa_addr->sa_family != clnt->sa_family || 234 !(ifap->ifa_flags & IFF_UP)) 235 continue; 236 if (!(ifap->ifa_flags & IFF_LOOPBACK) && 237 !(ifap->ifa_flags & IFF_POINTOPOINT)) { 238 bestif = ifap; 239 break; 240 } 241 if (bestif == NULL) 242 bestif = ifap; 243 else if ((bestif->ifa_flags & IFF_LOOPBACK) && 244 !(ifap->ifa_flags & IFF_LOOPBACK)) 245 bestif = ifap; 246 } 247 ifap = bestif; 248 found: 249 switch (clnt->sa_family) { 250 case AF_INET: 251 memcpy(newsin, ifap->ifa_addr, clnt_sa->sa_len); 252 newsin->sin_port = servsin->sin_port; 253 tbuf.len = clnt_sa->sa_len; 254 tbuf.maxlen = sizeof (struct sockaddr_storage); 255 tbuf.buf = newsin; 256 break; 257 #ifdef INET6 258 case AF_INET6: 259 assert(newsin6); 260 memcpy(newsin6, ifsin6, clnt_sa->sa_len); 261 newsin6->sin6_port = servsin6->sin6_port; 262 tbuf.maxlen = sizeof (struct sockaddr_storage); 263 tbuf.len = clnt_sa->sa_len; 264 tbuf.buf = newsin6; 265 break; 266 #endif 267 default: 268 goto freeit; 269 } 270 if (ifap != NULL) 271 ret = taddr2uaddr(nconf, &tbuf); 272 freeit: 273 freenetconfigent(nconf); 274 free(serv_sa); 275 free(serv_nbp); 276 if (clnt_sa != NULL) 277 free(clnt_sa); 278 if (clnt_nbp != NULL) 279 free(clnt_nbp); 280 freeifaddrs(ifp); 281 282 #ifdef RPCBIND_DEBUG 283 if (debugging) 284 fprintf(stderr, "addrmerge: returning %s\n", ret); 285 #endif 286 return ret; 287 } 288 289 void 290 network_init() 291 { 292 #ifdef INET6 293 struct ifaddrs *ifap, *ifp; 294 struct ipv6_mreq mreq6; 295 unsigned int ifindex; 296 int 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 368 struct sockaddr * 369 local_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