1 /* $NetBSD: util.c,v 1.2 2011/06/11 18:03:17 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 <rump/rump.h> 51 #include <rump/rump_syscalls.h> 52 53 #include "rpcbind.h" 54 55 static struct sockaddr_in *local_in4; 56 #ifdef INET6 57 static struct sockaddr_in6 *local_in6; 58 #endif 59 60 static int bitmaskcmp(void *, void *, void *, int); 61 #ifdef INET6 62 static void in6_fillscopeid(struct sockaddr_in6 *); 63 #endif 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 /* 90 * Taken from ifconfig.c 91 */ 92 #ifdef INET6 93 static void 94 in6_fillscopeid(struct sockaddr_in6 *sin6) 95 { 96 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 97 sin6->sin6_scope_id = 98 ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); 99 sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; 100 } 101 } 102 #endif 103 104 char * 105 addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, 106 char *netid) 107 { 108 struct ifaddrs *ifap, *ifp, *bestif; 109 #ifdef INET6 110 struct sockaddr_in6 *servsin6, *sin6mask, *clntsin6, *ifsin6, *realsin6; 111 struct sockaddr_in6 *newsin6; 112 #endif 113 struct sockaddr_in *servsin, *sinmask, *clntsin, *newsin, *ifsin; 114 struct netbuf *serv_nbp, *clnt_nbp = NULL, tbuf; 115 struct sockaddr *serv_sa; 116 struct sockaddr *clnt_sa; 117 struct sockaddr_storage ss; 118 struct netconfig *nconf; 119 struct sockaddr *clnt = caller->buf; 120 char *ret = NULL; 121 122 #ifdef INET6 123 servsin6 = ifsin6 = newsin6 = NULL; /* XXXGCC -Wuninitialized */ 124 #endif 125 servsin = newsin = NULL; /* XXXGCC -Wuninitialized */ 126 127 #ifdef RPCBIND_DEBUG 128 if (debugging) 129 fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, 130 clnt_uaddr, netid); 131 #endif 132 nconf = getnetconfigent(netid); 133 if (nconf == NULL) 134 return NULL; 135 136 /* 137 * Local merge, just return a duplicate. 138 */ 139 if (clnt_uaddr != NULL && strncmp(clnt_uaddr, "0.0.0.0.", 8) == 0) 140 return strdup(clnt_uaddr); 141 142 serv_nbp = uaddr2taddr(nconf, serv_uaddr); 143 if (serv_nbp == NULL) 144 return NULL; 145 146 serv_sa = (struct sockaddr *)serv_nbp->buf; 147 if (clnt_uaddr != NULL) { 148 clnt_nbp = uaddr2taddr(nconf, clnt_uaddr); 149 if (clnt_nbp == NULL) { 150 free(serv_nbp); 151 return NULL; 152 } 153 clnt_sa = (struct sockaddr *)clnt_nbp->buf; 154 if (clnt_sa->sa_family == AF_LOCAL) { 155 free(serv_nbp); 156 free(clnt_nbp); 157 free(clnt_sa); 158 return strdup(serv_uaddr); 159 } 160 } else { 161 clnt_sa = (struct sockaddr *) 162 malloc(sizeof (struct sockaddr_storage)); 163 memcpy(clnt_sa, clnt, clnt->sa_len); 164 } 165 166 if (getifaddrs(&ifp) < 0) { 167 free(serv_nbp); 168 free(clnt_sa); 169 if (clnt_nbp != NULL) 170 free(clnt_nbp); 171 return 0; 172 } 173 174 /* 175 * Loop through all interfaces. For each interface, see if the 176 * network portion of its address is equal to that of the client. 177 * If so, we have found the interface that we want to use. 178 */ 179 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 180 if (ifap->ifa_addr->sa_family != clnt->sa_family || 181 !(ifap->ifa_flags & IFF_UP)) 182 continue; 183 184 switch (clnt->sa_family) { 185 case AF_INET: 186 /* 187 * realsin: address that recvfrom gave us. 188 * ifsin: address of interface being examined. 189 * clntsin: address that client want us to contact 190 * it on 191 * servsin: local address of RPC service. 192 * sinmask: netmask of this interface 193 * newsin: initially a copy of clntsin, eventually 194 * the merged address 195 */ 196 servsin = (struct sockaddr_in *)serv_sa; 197 clntsin = (struct sockaddr_in *)clnt_sa; 198 sinmask = (struct sockaddr_in *)ifap->ifa_netmask; 199 newsin = (struct sockaddr_in *)&ss; 200 ifsin = (struct sockaddr_in *)ifap->ifa_addr; 201 if (!bitmaskcmp(&ifsin->sin_addr, &clntsin->sin_addr, 202 &sinmask->sin_addr, sizeof (struct in_addr))) { 203 goto found; 204 } 205 break; 206 #ifdef INET6 207 case AF_INET6: 208 /* 209 * realsin6: address that recvfrom gave us. 210 * ifsin6: address of interface being examined. 211 * clntsin6: address that client want us to contact 212 * it on 213 * servsin6: local address of RPC service. 214 * sin6mask: netmask of this interface 215 * newsin6: initially a copy of clntsin, eventually 216 * the merged address 217 * 218 * For v6 link local addresses, if the client contacted 219 * us via a link-local address, and wants us to reply 220 * to one, use the scope id to see which one. 221 */ 222 realsin6 = (struct sockaddr_in6 *)clnt; 223 ifsin6 = (struct sockaddr_in6 *)ifap->ifa_addr; 224 in6_fillscopeid(ifsin6); 225 clntsin6 = (struct sockaddr_in6 *)clnt_sa; 226 servsin6 = (struct sockaddr_in6 *)serv_sa; 227 sin6mask = (struct sockaddr_in6 *)ifap->ifa_netmask; 228 newsin6 = (struct sockaddr_in6 *)&ss; 229 if (IN6_IS_ADDR_LINKLOCAL(&ifsin6->sin6_addr) && 230 IN6_IS_ADDR_LINKLOCAL(&realsin6->sin6_addr) && 231 IN6_IS_ADDR_LINKLOCAL(&clntsin6->sin6_addr)) { 232 if (ifsin6->sin6_scope_id != 233 realsin6->sin6_scope_id) 234 continue; 235 goto found; 236 } 237 if (!bitmaskcmp(&ifsin6->sin6_addr, 238 &clntsin6->sin6_addr, &sin6mask->sin6_addr, 239 sizeof (struct in6_addr))) 240 goto found; 241 break; 242 #endif 243 default: 244 goto freeit; 245 } 246 } 247 /* 248 * Didn't find anything. Get the first possibly useful interface, 249 * preferring "normal" interfaces to point-to-point and loopback 250 * ones. 251 */ 252 bestif = NULL; 253 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 254 if (ifap->ifa_addr->sa_family != clnt->sa_family || 255 !(ifap->ifa_flags & IFF_UP)) 256 continue; 257 if (!(ifap->ifa_flags & IFF_LOOPBACK) && 258 !(ifap->ifa_flags & IFF_POINTOPOINT)) { 259 bestif = ifap; 260 break; 261 } 262 if (bestif == NULL) 263 bestif = ifap; 264 else if ((bestif->ifa_flags & IFF_LOOPBACK) && 265 !(ifap->ifa_flags & IFF_LOOPBACK)) 266 bestif = ifap; 267 } 268 ifap = bestif; 269 found: 270 switch (clnt->sa_family) { 271 case AF_INET: 272 memcpy(newsin, ifap->ifa_addr, clnt_sa->sa_len); 273 newsin->sin_port = servsin->sin_port; 274 tbuf.len = clnt_sa->sa_len; 275 tbuf.maxlen = sizeof (struct sockaddr_storage); 276 tbuf.buf = newsin; 277 break; 278 #ifdef INET6 279 case AF_INET6: 280 assert(newsin6); 281 memcpy(newsin6, ifsin6, clnt_sa->sa_len); 282 newsin6->sin6_port = servsin6->sin6_port; 283 tbuf.maxlen = sizeof (struct sockaddr_storage); 284 tbuf.len = clnt_sa->sa_len; 285 tbuf.buf = newsin6; 286 break; 287 #endif 288 default: 289 goto freeit; 290 } 291 if (ifap != NULL) 292 ret = taddr2uaddr(nconf, &tbuf); 293 freeit: 294 freenetconfigent(nconf); 295 free(serv_sa); 296 free(serv_nbp); 297 if (clnt_sa != NULL) 298 free(clnt_sa); 299 if (clnt_nbp != NULL) 300 free(clnt_nbp); 301 freeifaddrs(ifp); 302 303 #ifdef RPCBIND_DEBUG 304 if (debugging) 305 fprintf(stderr, "addrmerge: returning %s\n", ret); 306 #endif 307 return ret; 308 } 309 310 void 311 network_init() 312 { 313 #ifdef INET6 314 struct ifaddrs *ifap, *ifp; 315 struct ipv6_mreq mreq6; 316 int ifindex, s; 317 #endif 318 int ecode; 319 struct addrinfo hints, *res; 320 321 memset(&hints, 0, sizeof hints); 322 hints.ai_family = AF_INET; 323 if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { 324 if (debugging) 325 fprintf(stderr, "can't get local ip4 address: %s\n", 326 gai_strerror(ecode)); 327 } else { 328 local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4); 329 if (local_in4 == NULL) { 330 if (debugging) 331 fprintf(stderr, "can't alloc local ip4 addr\n"); 332 } 333 memcpy(local_in4, res->ai_addr, sizeof *local_in4); 334 } 335 336 #ifdef INET6 337 hints.ai_family = AF_INET6; 338 if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { 339 if (debugging) 340 fprintf(stderr, "can't get local ip6 address: %s\n", 341 gai_strerror(ecode)); 342 } else { 343 local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6); 344 if (local_in6 == NULL) { 345 if (debugging) 346 fprintf(stderr, "can't alloc local ip6 addr\n"); 347 } 348 memcpy(local_in6, res->ai_addr, sizeof *local_in6); 349 } 350 351 /* 352 * Now join the RPC ipv6 multicast group on all interfaces. 353 */ 354 if (getifaddrs(&ifp) < 0) 355 return; 356 357 mreq6.ipv6mr_interface = 0; 358 inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr); 359 360 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 361 362 /* 363 * Loop through all interfaces. For each interface, see if the 364 * network portion of its address is equal to that of the client. 365 * If so, we have found the interface that we want to use. 366 */ 367 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 368 if (ifap->ifa_addr->sa_family != AF_INET6 || 369 !(ifap->ifa_flags & IFF_MULTICAST)) 370 continue; 371 ifindex = if_nametoindex(ifap->ifa_name); 372 if (ifindex == mreq6.ipv6mr_interface) 373 /* 374 * Already did this one. 375 */ 376 continue; 377 mreq6.ipv6mr_interface = ifindex; 378 if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, 379 sizeof mreq6) < 0) 380 if (debugging) 381 warn("setsockopt v6 multicast"); 382 } 383 #endif 384 385 /* close(s); */ 386 } 387 388 struct sockaddr * 389 local_sa(int af) 390 { 391 switch (af) { 392 case AF_INET: 393 return (struct sockaddr *)local_in4; 394 #ifdef INET6 395 case AF_INET6: 396 return (struct sockaddr *)local_in6; 397 #endif 398 default: 399 return NULL; 400 } 401 } 402