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