2 * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $
7 * Copyright (c) 2000 The NetBSD Foundation, Inc.
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Frank van der Linden.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
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.
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/queue.h>
39 #include <netinet/in.h>
48 #include <netconfig.h>
50 #include <arpa/inet.h>
54 static struct sockaddr_in
*local_in4
;
56 static struct sockaddr_in6
*local_in6
;
59 static int bitmaskcmp(struct sockaddr
*, struct sockaddr
*, struct sockaddr
*);
62 * For all bits set in "mask", compare the corresponding bits in
63 * "dst" and "src", and see if they match. Returns 0 if the addresses
67 bitmaskcmp(struct sockaddr
*dst
, struct sockaddr
*src
, struct sockaddr
*mask
)
70 u_int8_t
*p1
, *p2
, *netmask
;
73 if (dst
->sa_family
!= src
->sa_family
||
74 dst
->sa_family
!= mask
->sa_family
)
77 switch (dst
->sa_family
) {
79 p1
= (uint8_t*) &SA2SINADDR(dst
);
80 p2
= (uint8_t*) &SA2SINADDR(src
);
81 netmask
= (uint8_t*) &SA2SINADDR(mask
);
82 bytelen
= sizeof(struct in_addr
);
86 p1
= (uint8_t*) &SA2SIN6ADDR(dst
);
87 p2
= (uint8_t*) &SA2SIN6ADDR(src
);
88 netmask
= (uint8_t*) &SA2SIN6ADDR(mask
);
89 bytelen
= sizeof(struct in6_addr
);
96 for (i
= 0; i
< bytelen
; i
++)
97 if ((p1
[i
] & netmask
[i
]) != (p2
[i
] & netmask
[i
]))
103 * Find a server address that can be used by `caller' to contact
104 * the local service specified by `serv_uaddr'. If `clnt_uaddr' is
105 * non-NULL, it is used instead of `caller' as a hint suggesting
106 * the best address (e.g. the `r_addr' field of an rpc, which
107 * contains the rpcbind server address that the caller used).
109 * Returns the best server address as a malloc'd "universal address"
110 * string which should be freed by the caller. On error, returns NULL.
113 addrmerge(struct netbuf
*caller
, const char *serv_uaddr
, const char *clnt_uaddr
,
116 struct ifaddrs
*ifap
, *ifp
= NULL
, *bestif
;
117 struct netbuf
*serv_nbp
= NULL
, *hint_nbp
= NULL
, tbuf
;
118 struct sockaddr
*caller_sa
, *hint_sa
, *ifsa
, *ifmasksa
, *serv_sa
;
119 struct sockaddr_storage ss
;
120 struct netconfig
*nconf
;
121 char *caller_uaddr
= NULL
;
123 const char *hint_uaddr
= NULL
;
130 fprintf(stderr
, "addrmerge(caller, %s, %s, %s\n", serv_uaddr
,
131 clnt_uaddr
== NULL
? "NULL" : clnt_uaddr
, netid
);
133 caller_sa
= caller
->buf
;
134 if ((nconf
= rpcbind_get_conf(netid
)) == NULL
)
136 if ((caller_uaddr
= taddr2uaddr(nconf
, caller
)) == NULL
)
140 * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its
141 * address family is different from that of the caller.
144 if (clnt_uaddr
!= NULL
) {
146 hint_uaddr
= clnt_uaddr
;
148 if ((hint_nbp
= uaddr2taddr(nconf
, clnt_uaddr
)) == NULL
)
150 hint_sa
= hint_nbp
->buf
;
152 if (hint_sa
== NULL
|| hint_sa
->sa_family
!= caller_sa
->sa_family
) {
154 hint_uaddr
= caller_uaddr
;
156 hint_sa
= caller
->buf
;
161 fprintf(stderr
, "addrmerge: hint %s\n", hint_uaddr
);
163 /* Local caller, just return the server address. */
164 if (strncmp(caller_uaddr
, "0.0.0.0.", 8) == 0 ||
165 strncmp(caller_uaddr
, "::.", 3) == 0 || caller_uaddr
[0] == '/') {
166 ret
= strdup(serv_uaddr
);
170 if (getifaddrs(&ifp
) < 0)
174 * Loop through all interface addresses. We are listening to an address
175 * if any of the following are true:
176 * a) It's a loopback address
177 * b) It was specified with the -h command line option
178 * c) There were no -h command line options.
180 * Among addresses on which we are listening, choose in order of
181 * preference an address that is:
183 * a) Equal to the hint
184 * b) A link local address with the same scope ID as the client's
185 * address, if the client's address is also link local
186 * c) An address on the same subnet as the client's address
187 * d) A non-localhost, non-p2p address
188 * e) Any usable address
192 for (ifap
= ifp
; ifap
!= NULL
; ifap
= ifap
->ifa_next
) {
193 ifsa
= ifap
->ifa_addr
;
194 ifmasksa
= ifap
->ifa_netmask
;
196 /* Skip addresses where we don't listen */
197 if (ifsa
== NULL
|| ifsa
->sa_family
!= hint_sa
->sa_family
||
198 !(ifap
->ifa_flags
& IFF_UP
))
201 if (!(ifap
->ifa_flags
& IFF_LOOPBACK
) && !listen_addr(ifsa
))
204 if ((hint_sa
->sa_family
== AF_INET
) &&
205 ((((struct sockaddr_in
*)hint_sa
)->sin_addr
.s_addr
==
206 ((struct sockaddr_in
*)ifsa
)->sin_addr
.s_addr
))) {
207 const int goodness
= 4;
209 bestif_goodness
= goodness
;
214 if ((hint_sa
->sa_family
== AF_INET6
) &&
215 (0 == memcmp(&((struct sockaddr_in6
*)hint_sa
)->sin6_addr
,
216 &((struct sockaddr_in6
*)ifsa
)->sin6_addr
,
217 sizeof(struct in6_addr
))) &&
218 (((struct sockaddr_in6
*)hint_sa
)->sin6_scope_id
==
219 (((struct sockaddr_in6
*)ifsa
)->sin6_scope_id
))) {
220 const int goodness
= 4;
222 bestif_goodness
= goodness
;
226 if (hint_sa
->sa_family
== AF_INET6
) {
228 * For v6 link local addresses, if the caller is on
229 * a link-local address then use the scope id to see
232 if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa
)) &&
233 IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa
)) &&
234 IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa
))) {
235 if (SA2SIN6(ifsa
)->sin6_scope_id
==
236 SA2SIN6(caller_sa
)->sin6_scope_id
) {
237 const int goodness
= 3;
239 if (bestif_goodness
< goodness
) {
241 bestif_goodness
= goodness
;
247 if (0 == bitmaskcmp(hint_sa
, ifsa
, ifmasksa
)) {
248 const int goodness
= 2;
250 if (bestif_goodness
< goodness
) {
252 bestif_goodness
= goodness
;
255 if (!(ifap
->ifa_flags
& (IFF_LOOPBACK
| IFF_POINTOPOINT
))) {
256 const int goodness
= 1;
258 if (bestif_goodness
< goodness
) {
260 bestif_goodness
= goodness
;
271 * Construct the new address using the address from
272 * `bestif', and the port number from `serv_uaddr'.
274 serv_nbp
= uaddr2taddr(nconf
, serv_uaddr
);
275 if (serv_nbp
== NULL
)
277 serv_sa
= serv_nbp
->buf
;
279 memcpy(&ss
, bestif
->ifa_addr
, bestif
->ifa_addr
->sa_len
);
280 switch (ss
.ss_family
) {
282 SA2SIN(&ss
)->sin_port
= SA2SIN(serv_sa
)->sin_port
;
286 SA2SIN6(&ss
)->sin6_port
= SA2SIN6(serv_sa
)->sin6_port
;
290 tbuf
.len
= ss
.ss_len
;
291 tbuf
.maxlen
= sizeof(ss
);
293 ret
= taddr2uaddr(nconf
, &tbuf
);
297 if (hint_nbp
!= NULL
) {
301 if (serv_nbp
!= NULL
) {
310 fprintf(stderr
, "addrmerge: returning %s\n", ret
);
319 struct ifaddrs
*ifap
, *ifp
;
320 struct ipv6_mreq mreq6
;
321 unsigned int ifindex
;
325 struct addrinfo hints
, *res
;
327 memset(&hints
, 0, sizeof hints
);
328 hints
.ai_family
= AF_INET
;
329 if ((ecode
= getaddrinfo(NULL
, "sunrpc", &hints
, &res
))) {
331 fprintf(stderr
, "can't get local ip4 address: %s\n",
332 gai_strerror(ecode
));
334 local_in4
= (struct sockaddr_in
*)malloc(sizeof *local_in4
);
335 if (local_in4
== NULL
) {
337 fprintf(stderr
, "can't alloc local ip4 addr\n");
340 memcpy(local_in4
, res
->ai_addr
, sizeof *local_in4
);
345 hints
.ai_family
= AF_INET6
;
346 if ((ecode
= getaddrinfo(NULL
, "sunrpc", &hints
, &res
))) {
348 fprintf(stderr
, "can't get local ip6 address: %s\n",
349 gai_strerror(ecode
));
351 local_in6
= (struct sockaddr_in6
*)malloc(sizeof *local_in6
);
352 if (local_in6
== NULL
) {
354 fprintf(stderr
, "can't alloc local ip6 addr\n");
357 memcpy(local_in6
, res
->ai_addr
, sizeof *local_in6
);
362 * Now join the RPC ipv6 multicast group on all interfaces.
364 if (getifaddrs(&ifp
) < 0)
367 mreq6
.ipv6mr_interface
= 0;
368 inet_pton(AF_INET6
, RPCB_MULTICAST_ADDR
, &mreq6
.ipv6mr_multiaddr
);
370 s
= socket(AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
);
373 fprintf(stderr
, "couldn't create ip6 socket");
378 * Loop through all interfaces. For each IPv6 multicast-capable
379 * interface, join the RPC multicast group on that interface.
381 for (ifap
= ifp
; ifap
!= NULL
; ifap
= ifap
->ifa_next
) {
382 if (ifap
->ifa_addr
->sa_family
!= AF_INET6
||
383 !(ifap
->ifa_flags
& IFF_MULTICAST
))
385 ifindex
= if_nametoindex(ifap
->ifa_name
);
386 if (ifindex
== mreq6
.ipv6mr_interface
)
388 * Already did this one.
391 mreq6
.ipv6mr_interface
= ifindex
;
392 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &mreq6
,
395 perror("setsockopt v6 multicast");
409 return (struct sockaddr
*)local_in4
;
412 return (struct sockaddr
*)local_in6
;