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