xref: /netbsd-src/usr.sbin/rpcbind/util.c (revision aa97815be0953bb5e277da3a05e0905e1a0955f7)
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