1170620Sbms/*-
2189335Sbms * Copyright (c) 2007-2009 Bruce Simpson.
3189335Sbms * All rights reserved.
4170620Sbms *
5170620Sbms * Redistribution and use in source and binary forms, with or without
6170620Sbms * modification, are permitted provided that the following conditions
7170620Sbms * are met:
8170620Sbms * 1. Redistributions of source code must retain the above copyright
9170620Sbms *    notice, this list of conditions and the following disclaimer.
10170620Sbms * 2. Redistributions in binary form must reproduce the above copyright
11170620Sbms *    notice, this list of conditions and the following disclaimer in the
12170620Sbms *    documentation and/or other materials provided with the distribution.
13170620Sbms *
14189335Sbms * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15189335Sbms * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16189335Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17189335Sbms * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18189335Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19189335Sbms * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20189335Sbms * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21189335Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22189335Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23189335Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24189335Sbms * SUCH DAMAGE.
25170620Sbms */
26170620Sbms
27170620Sbms#include <sys/cdefs.h>
28170620Sbms__FBSDID("$FreeBSD$");
29170620Sbms
30170620Sbms#include "namespace.h"
31170620Sbms
32170620Sbms#include <sys/types.h>
33170620Sbms#include <sys/param.h>
34170620Sbms#include <sys/ioctl.h>
35170620Sbms#include <sys/socket.h>
36170620Sbms
37170620Sbms#include <net/if_dl.h>
38170620Sbms#include <net/if.h>
39170620Sbms#include <netinet/in.h>
40170620Sbms#include <netinet/in_systm.h>
41170620Sbms#include <netinet/ip.h>
42170620Sbms#include <netinet/ip_var.h>
43170620Sbms
44170620Sbms#include <assert.h>
45170620Sbms#include <errno.h>
46170620Sbms#include <ifaddrs.h>
47170620Sbms#include <stdlib.h>
48170620Sbms#include <string.h>
49170620Sbms
50170620Sbms#include "un-namespace.h"
51170620Sbms
52170620Sbms/*
53170620Sbms * Advanced (Full-state) multicast group membership APIs [RFC3678]
54170620Sbms * Currently this module assumes IPv4 support (INET) in the base system.
55170620Sbms */
56170620Sbms#ifndef INET
57170620Sbms#define INET
58170620Sbms#endif
59170620Sbms
60170620Sbmsunion sockunion {
61170620Sbms	struct sockaddr_storage	ss;
62170620Sbms	struct sockaddr		sa;
63170620Sbms	struct sockaddr_dl	sdl;
64170620Sbms#ifdef INET
65170620Sbms	struct sockaddr_in	sin;
66170620Sbms#endif
67170620Sbms#ifdef INET6
68170620Sbms	struct sockaddr_in6	sin6;
69170620Sbms#endif
70170620Sbms};
71170620Sbmstypedef union sockunion sockunion_t;
72170620Sbms
73170620Sbms#ifndef MIN
74170620Sbms#define	MIN(a, b)	((a) < (b) ? (a) : (b))
75170620Sbms#endif
76170620Sbms
77170620Sbms/*
78170620Sbms * Internal: Map an IPv4 unicast address to an interface index.
79170620Sbms * This is quite inefficient so it is recommended applications use
80170620Sbms * the newer, more portable, protocol independent API.
81170620Sbms */
82170620Sbmsstatic uint32_t
83170620Sbms__inaddr_to_index(in_addr_t ifaddr)
84170620Sbms{
85170620Sbms	struct ifaddrs	*ifa;
86170620Sbms	struct ifaddrs	*ifaddrs;
87170620Sbms	char		*ifname;
88170620Sbms	int		 ifindex;
89170620Sbms	sockunion_t	*psu;
90170620Sbms
91170620Sbms	if (getifaddrs(&ifaddrs) < 0)
92170620Sbms		return (0);
93170620Sbms
94170620Sbms	ifindex = 0;
95170620Sbms	ifname = NULL;
96170620Sbms
97170620Sbms	/*
98170620Sbms	 * Pass #1: Find the ifaddr entry corresponding to the
99170620Sbms	 * supplied IPv4 address. We should really use the ifindex
100170620Sbms	 * consistently for matches, however it is not available to
101170620Sbms	 * us on this pass.
102170620Sbms	 */
103170620Sbms	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
104170620Sbms		psu = (sockunion_t *)ifa->ifa_addr;
105170620Sbms		if (psu && psu->ss.ss_family == AF_INET &&
106170620Sbms		    psu->sin.sin_addr.s_addr == ifaddr) {
107170620Sbms			ifname = ifa->ifa_name;
108170620Sbms			break;
109170620Sbms		}
110170620Sbms	}
111170620Sbms	if (ifname == NULL)
112170620Sbms		goto out;
113170620Sbms
114170620Sbms	/*
115170620Sbms	 * Pass #2: Find the index of the interface matching the name
116170620Sbms	 * we obtained from looking up the IPv4 ifaddr in pass #1.
117170620Sbms	 * There must be a better way of doing this.
118170620Sbms	 */
119170620Sbms	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
120170620Sbms		psu = (sockunion_t *)ifa->ifa_addr;
121170620Sbms		if (psu && psu->ss.ss_family == AF_LINK &&
122170620Sbms		    strcmp(ifa->ifa_name, ifname) == 0) {
123170620Sbms			ifindex = psu->sdl.sdl_index;
124170620Sbms			break;
125170620Sbms		}
126170620Sbms	}
127170620Sbms	assert(ifindex != 0);
128170620Sbms
129170620Sbmsout:
130170620Sbms	freeifaddrs(ifaddrs);
131170620Sbms	return (ifindex);
132170620Sbms}
133170620Sbms
134170620Sbms/*
135170620Sbms * Set IPv4 source filter list in use on socket.
136170620Sbms *
137170620Sbms * Stubbed to setsourcefilter(). Performs conversion of structures which
138170620Sbms * may be inefficient; applications are encouraged to use the
139170620Sbms * protocol-independent API.
140170620Sbms */
141170620Sbmsint
142170620Sbmssetipv4sourcefilter(int s, struct in_addr interface, struct in_addr group,
143170620Sbms    uint32_t fmode, uint32_t numsrc, struct in_addr *slist)
144170620Sbms{
145170620Sbms#ifdef INET
146170620Sbms	sockunion_t	 tmpgroup;
147170620Sbms	struct in_addr	*pina;
148170620Sbms	sockunion_t	*psu, *tmpslist;
149170620Sbms	int		 err;
150170620Sbms	size_t		 i;
151170620Sbms	uint32_t	 ifindex;
152170620Sbms
153170620Sbms	assert(s != -1);
154170620Sbms
155170620Sbms	tmpslist = NULL;
156170620Sbms
157170620Sbms	if (!IN_MULTICAST(ntohl(group.s_addr)) ||
158170620Sbms	    (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE)) {
159170620Sbms		errno = EINVAL;
160170620Sbms		return (-1);
161170620Sbms	}
162170620Sbms
163170620Sbms	ifindex = __inaddr_to_index(interface.s_addr);
164170620Sbms	if (ifindex == 0) {
165170620Sbms		errno = EADDRNOTAVAIL;
166170620Sbms		return (-1);
167170620Sbms	}
168170620Sbms
169170620Sbms	memset(&tmpgroup, 0, sizeof(sockunion_t));
170170620Sbms	tmpgroup.sin.sin_family = AF_INET;
171170620Sbms	tmpgroup.sin.sin_len = sizeof(struct sockaddr_in);
172170620Sbms	tmpgroup.sin.sin_addr = group;
173170620Sbms
174170620Sbms	if (numsrc != 0 || slist != NULL) {
175170620Sbms		tmpslist = calloc(numsrc, sizeof(sockunion_t));
176170620Sbms		if (tmpslist == NULL) {
177170620Sbms			errno = ENOMEM;
178170620Sbms			return (-1);
179170620Sbms		}
180170620Sbms
181170620Sbms		pina = slist;
182170620Sbms		psu = tmpslist;
183170620Sbms		for (i = 0; i < numsrc; i++, pina++, psu++) {
184170620Sbms			psu->sin.sin_family = AF_INET;
185170620Sbms			psu->sin.sin_len = sizeof(struct sockaddr_in);
186170620Sbms			psu->sin.sin_addr = *pina;
187170620Sbms		}
188170620Sbms	}
189170620Sbms
190170620Sbms	err = setsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup,
191170620Sbms	    sizeof(struct sockaddr_in), fmode, numsrc,
192170620Sbms	    (struct sockaddr_storage *)tmpslist);
193170620Sbms
194170620Sbms	if (tmpslist != NULL)
195170620Sbms		free(tmpslist);
196170620Sbms
197170620Sbms	return (err);
198170620Sbms#else /* !INET */
199170620Sbms	return (EAFNOSUPPORT);
200170620Sbms#endif /* INET */
201170620Sbms}
202170620Sbms
203170620Sbms/*
204170620Sbms * Get IPv4 source filter list in use on socket.
205170620Sbms *
206170620Sbms * Stubbed to getsourcefilter(). Performs conversion of structures which
207170620Sbms * may be inefficient; applications are encouraged to use the
208170620Sbms * protocol-independent API.
209170620Sbms * An slist of NULL may be used for guessing the required buffer size.
210170620Sbms */
211170620Sbmsint
212170620Sbmsgetipv4sourcefilter(int s, struct in_addr interface, struct in_addr group,
213170620Sbms    uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist)
214170620Sbms{
215170620Sbms	sockunion_t	*psu, *tmpslist;
216170620Sbms	sockunion_t	 tmpgroup;
217170620Sbms	struct in_addr	*pina;
218170620Sbms	int		 err;
219170620Sbms	size_t		 i;
220170620Sbms	uint32_t	 ifindex, onumsrc;
221170620Sbms
222170620Sbms	assert(s != -1);
223170620Sbms	assert(fmode != NULL);
224170620Sbms	assert(numsrc != NULL);
225170620Sbms
226170620Sbms	onumsrc = *numsrc;
227170620Sbms	*numsrc = 0;
228170620Sbms	tmpslist = NULL;
229170620Sbms
230170620Sbms	if (!IN_MULTICAST(ntohl(group.s_addr)) ||
231170620Sbms	    (onumsrc != 0 && slist == NULL)) {
232170620Sbms		errno = EINVAL;
233170620Sbms		return (-1);
234170620Sbms	}
235170620Sbms
236170620Sbms	ifindex = __inaddr_to_index(interface.s_addr);
237170620Sbms	if (ifindex == 0) {
238170620Sbms		errno = EADDRNOTAVAIL;
239170620Sbms		return (-1);
240170620Sbms	}
241170620Sbms
242170620Sbms	memset(&tmpgroup, 0, sizeof(sockunion_t));
243170620Sbms	tmpgroup.sin.sin_family = AF_INET;
244170620Sbms	tmpgroup.sin.sin_len = sizeof(struct sockaddr_in);
245170620Sbms	tmpgroup.sin.sin_addr = group;
246170620Sbms
247170620Sbms	if (onumsrc != 0 || slist != NULL) {
248170620Sbms		tmpslist = calloc(onumsrc, sizeof(sockunion_t));
249170620Sbms		if (tmpslist == NULL) {
250170620Sbms			errno = ENOMEM;
251170620Sbms			return (-1);
252170620Sbms		}
253170620Sbms	}
254170620Sbms
255170620Sbms	err = getsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup,
256170620Sbms	    sizeof(struct sockaddr_in), fmode, numsrc,
257170620Sbms	    (struct sockaddr_storage *)tmpslist);
258170620Sbms
259170620Sbms	if (tmpslist != NULL && *numsrc != 0) {
260170620Sbms		pina = slist;
261170620Sbms		psu = tmpslist;
262170620Sbms		for (i = 0; i < MIN(onumsrc, *numsrc); i++, psu++) {
263170620Sbms			if (psu->ss.ss_family != AF_INET)
264170620Sbms				continue;
265170620Sbms			*pina++ = psu->sin.sin_addr;
266170620Sbms		}
267170620Sbms		free(tmpslist);
268170620Sbms	}
269170620Sbms
270170620Sbms	return (err);
271170620Sbms}
272170620Sbms
273170620Sbms/*
274170620Sbms * Set protocol-independent source filter list in use on socket.
275170620Sbms */
276170620Sbmsint
277170620Sbmssetsourcefilter(int s, uint32_t interface, struct sockaddr *group,
278170620Sbms    socklen_t grouplen, uint32_t fmode, uint32_t numsrc,
279170620Sbms    struct sockaddr_storage *slist)
280170620Sbms{
281170620Sbms	struct __msfilterreq	 msfr;
282170620Sbms	sockunion_t		*psu;
283170620Sbms	int			 level, optname;
284170620Sbms
285170620Sbms	if (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE) {
286170620Sbms		errno = EINVAL;
287170620Sbms		return (-1);
288170620Sbms	}
289170620Sbms
290170620Sbms	psu = (sockunion_t *)group;
291170620Sbms	switch (psu->ss.ss_family) {
292170620Sbms#ifdef INET
293170620Sbms	case AF_INET:
294170620Sbms		if ((grouplen != sizeof(struct sockaddr_in) ||
295170620Sbms		    !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) {
296170620Sbms			errno = EINVAL;
297170620Sbms			return (-1);
298170620Sbms		}
299170620Sbms		level = IPPROTO_IP;
300170620Sbms		optname = IP_MSFILTER;
301170620Sbms		break;
302170620Sbms#endif
303170620Sbms#ifdef INET6
304170620Sbms	case AF_INET6:
305170620Sbms		if (grouplen != sizeof(struct sockaddr_in6) ||
306170625Sbms		    !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) {
307170620Sbms			errno = EINVAL;
308170620Sbms			return (-1);
309170620Sbms		}
310170620Sbms		level = IPPROTO_IPV6;
311170620Sbms		optname = IPV6_MSFILTER;
312170620Sbms		break;
313170620Sbms#endif
314170620Sbms	default:
315170620Sbms		errno = EAFNOSUPPORT;
316170620Sbms		return (-1);
317170620Sbms	}
318170620Sbms
319170620Sbms	memset(&msfr, 0, sizeof(msfr));
320170620Sbms	msfr.msfr_ifindex = interface;
321170620Sbms	msfr.msfr_fmode = fmode;
322170620Sbms	msfr.msfr_nsrcs = numsrc;
323170620Sbms	memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len);
324170620Sbms	msfr.msfr_srcs = slist;		/* pointer */
325170620Sbms
326171197Speter	return (_setsockopt(s, level, optname, &msfr, sizeof(msfr)));
327170620Sbms}
328170620Sbms
329170620Sbms/*
330170620Sbms * Get protocol-independent source filter list in use on socket.
331170620Sbms * An slist of NULL may be used for guessing the required buffer size.
332170620Sbms */
333170620Sbmsint
334170620Sbmsgetsourcefilter(int s, uint32_t interface, struct sockaddr *group,
335170620Sbms    socklen_t grouplen, uint32_t *fmode, uint32_t *numsrc,
336170620Sbms    struct sockaddr_storage *slist)
337170620Sbms{
338170620Sbms	struct __msfilterreq	 msfr;
339170620Sbms	sockunion_t		*psu;
340191654Sbms	int			 err, level, nsrcs, optlen, optname;
341170620Sbms
342170620Sbms	if (interface == 0 || group == NULL || numsrc == NULL ||
343170620Sbms	    fmode == NULL) {
344170620Sbms		errno = EINVAL;
345170620Sbms		return (-1);
346170620Sbms	}
347170620Sbms
348191654Sbms	nsrcs = *numsrc;
349170620Sbms	*numsrc = 0;
350170620Sbms	*fmode = 0;
351170620Sbms
352170620Sbms	psu = (sockunion_t *)group;
353170620Sbms	switch (psu->ss.ss_family) {
354170620Sbms#ifdef INET
355170620Sbms	case AF_INET:
356170620Sbms		if ((grouplen != sizeof(struct sockaddr_in) ||
357170620Sbms		    !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) {
358170620Sbms			errno = EINVAL;
359170620Sbms			return (-1);
360170620Sbms		}
361170620Sbms		level = IPPROTO_IP;
362170620Sbms		optname = IP_MSFILTER;
363170620Sbms		break;
364170620Sbms#endif
365170620Sbms#ifdef INET6
366170620Sbms	case AF_INET6:
367170620Sbms		if (grouplen != sizeof(struct sockaddr_in6) ||
368170625Sbms		    !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) {
369170620Sbms			errno = EINVAL;
370170620Sbms			return (-1);
371170620Sbms		}
372170620Sbms		level = IPPROTO_IPV6;
373170620Sbms		optname = IPV6_MSFILTER;
374170620Sbms		break;
375170620Sbms#endif
376170620Sbms	default:
377170620Sbms		errno = EAFNOSUPPORT;
378170620Sbms		return (-1);
379170620Sbms		break;
380170620Sbms	}
381170620Sbms
382170620Sbms	optlen = sizeof(struct __msfilterreq);
383170620Sbms	memset(&msfr, 0, optlen);
384170620Sbms	msfr.msfr_ifindex = interface;
385170620Sbms	msfr.msfr_fmode = 0;
386191654Sbms	msfr.msfr_nsrcs = nsrcs;
387170620Sbms	memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len);
388170620Sbms
389170620Sbms	/*
390170620Sbms	 * msfr_srcs is a pointer to a vector of sockaddr_storage. It
391170620Sbms	 * may be NULL. The kernel will always return the total number
392170620Sbms	 * of filter entries for the group in msfr.msfr_nsrcs.
393170620Sbms	 */
394170620Sbms	msfr.msfr_srcs = slist;
395171197Speter	err = _getsockopt(s, level, optname, &msfr, &optlen);
396170620Sbms	if (err == 0) {
397170620Sbms		*numsrc = msfr.msfr_nsrcs;
398170620Sbms		*fmode = msfr.msfr_fmode;
399170620Sbms	}
400170620Sbms
401170620Sbms	return (err);
402170620Sbms}
403