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/param.h>
33170620Sbms#include <sys/ioctl.h>
34170620Sbms#include <sys/socket.h>
35170620Sbms
36170620Sbms#include <net/if_dl.h>
37170620Sbms#include <net/if.h>
38170620Sbms#include <netinet/in.h>
39170620Sbms#include <netinet/in_systm.h>
40170620Sbms#include <netinet/ip.h>
41170620Sbms#include <netinet/ip_var.h>
42170620Sbms
43170620Sbms#include <assert.h>
44170620Sbms#include <errno.h>
45170620Sbms#include <ifaddrs.h>
46170620Sbms#include <stdlib.h>
47170620Sbms#include <string.h>
48170620Sbms
49170620Sbms#include "un-namespace.h"
50170620Sbms
51170620Sbms/*
52170620Sbms * Advanced (Full-state) multicast group membership APIs [RFC3678]
53170620Sbms * Currently this module assumes IPv4 support (INET) in the base system.
54170620Sbms */
55170620Sbms#ifndef INET
56170620Sbms#define INET
57170620Sbms#endif
58170620Sbms
59170620Sbmsunion sockunion {
60170620Sbms	struct sockaddr_storage	ss;
61170620Sbms	struct sockaddr		sa;
62170620Sbms	struct sockaddr_dl	sdl;
63170620Sbms#ifdef INET
64170620Sbms	struct sockaddr_in	sin;
65170620Sbms#endif
66170620Sbms#ifdef INET6
67170620Sbms	struct sockaddr_in6	sin6;
68170620Sbms#endif
69170620Sbms};
70170620Sbmstypedef union sockunion sockunion_t;
71170620Sbms
72170620Sbms#ifndef MIN
73170620Sbms#define	MIN(a, b)	((a) < (b) ? (a) : (b))
74170620Sbms#endif
75170620Sbms
76170620Sbms/*
77170620Sbms * Internal: Map an IPv4 unicast address to an interface index.
78170620Sbms * This is quite inefficient so it is recommended applications use
79170620Sbms * the newer, more portable, protocol independent API.
80170620Sbms */
81170620Sbmsstatic uint32_t
82170620Sbms__inaddr_to_index(in_addr_t ifaddr)
83170620Sbms{
84170620Sbms	struct ifaddrs	*ifa;
85170620Sbms	struct ifaddrs	*ifaddrs;
86170620Sbms	char		*ifname;
87170620Sbms	int		 ifindex;
88170620Sbms	sockunion_t	*psu;
89170620Sbms
90170620Sbms	if (getifaddrs(&ifaddrs) < 0)
91170620Sbms		return (0);
92170620Sbms
93170620Sbms	ifindex = 0;
94170620Sbms	ifname = NULL;
95170620Sbms
96170620Sbms	/*
97170620Sbms	 * Pass #1: Find the ifaddr entry corresponding to the
98170620Sbms	 * supplied IPv4 address. We should really use the ifindex
99170620Sbms	 * consistently for matches, however it is not available to
100170620Sbms	 * us on this pass.
101170620Sbms	 */
102170620Sbms	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
103170620Sbms		psu = (sockunion_t *)ifa->ifa_addr;
104170620Sbms		if (psu && psu->ss.ss_family == AF_INET &&
105170620Sbms		    psu->sin.sin_addr.s_addr == ifaddr) {
106170620Sbms			ifname = ifa->ifa_name;
107170620Sbms			break;
108170620Sbms		}
109170620Sbms	}
110170620Sbms	if (ifname == NULL)
111170620Sbms		goto out;
112170620Sbms
113170620Sbms	/*
114170620Sbms	 * Pass #2: Find the index of the interface matching the name
115170620Sbms	 * we obtained from looking up the IPv4 ifaddr in pass #1.
116170620Sbms	 * There must be a better way of doing this.
117170620Sbms	 */
118170620Sbms	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
119170620Sbms		psu = (sockunion_t *)ifa->ifa_addr;
120170620Sbms		if (psu && psu->ss.ss_family == AF_LINK &&
121170620Sbms		    strcmp(ifa->ifa_name, ifname) == 0) {
122235640Smarcel			ifindex = LLINDEX(&psu->sdl);
123170620Sbms			break;
124170620Sbms		}
125170620Sbms	}
126170620Sbms	assert(ifindex != 0);
127170620Sbms
128170620Sbmsout:
129170620Sbms	freeifaddrs(ifaddrs);
130170620Sbms	return (ifindex);
131170620Sbms}
132170620Sbms
133170620Sbms/*
134170620Sbms * Set IPv4 source filter list in use on socket.
135170620Sbms *
136170620Sbms * Stubbed to setsourcefilter(). Performs conversion of structures which
137170620Sbms * may be inefficient; applications are encouraged to use the
138170620Sbms * protocol-independent API.
139170620Sbms */
140170620Sbmsint
141170620Sbmssetipv4sourcefilter(int s, struct in_addr interface, struct in_addr group,
142170620Sbms    uint32_t fmode, uint32_t numsrc, struct in_addr *slist)
143170620Sbms{
144170620Sbms#ifdef INET
145170620Sbms	sockunion_t	 tmpgroup;
146170620Sbms	struct in_addr	*pina;
147170620Sbms	sockunion_t	*psu, *tmpslist;
148170620Sbms	int		 err;
149170620Sbms	size_t		 i;
150170620Sbms	uint32_t	 ifindex;
151170620Sbms
152170620Sbms	assert(s != -1);
153170620Sbms
154170620Sbms	tmpslist = NULL;
155170620Sbms
156170620Sbms	if (!IN_MULTICAST(ntohl(group.s_addr)) ||
157170620Sbms	    (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE)) {
158170620Sbms		errno = EINVAL;
159170620Sbms		return (-1);
160170620Sbms	}
161170620Sbms
162170620Sbms	ifindex = __inaddr_to_index(interface.s_addr);
163170620Sbms	if (ifindex == 0) {
164170620Sbms		errno = EADDRNOTAVAIL;
165170620Sbms		return (-1);
166170620Sbms	}
167170620Sbms
168170620Sbms	memset(&tmpgroup, 0, sizeof(sockunion_t));
169170620Sbms	tmpgroup.sin.sin_family = AF_INET;
170170620Sbms	tmpgroup.sin.sin_len = sizeof(struct sockaddr_in);
171170620Sbms	tmpgroup.sin.sin_addr = group;
172170620Sbms
173170620Sbms	if (numsrc != 0 || slist != NULL) {
174170620Sbms		tmpslist = calloc(numsrc, sizeof(sockunion_t));
175170620Sbms		if (tmpslist == NULL) {
176170620Sbms			errno = ENOMEM;
177170620Sbms			return (-1);
178170620Sbms		}
179170620Sbms
180170620Sbms		pina = slist;
181170620Sbms		psu = tmpslist;
182170620Sbms		for (i = 0; i < numsrc; i++, pina++, psu++) {
183170620Sbms			psu->sin.sin_family = AF_INET;
184170620Sbms			psu->sin.sin_len = sizeof(struct sockaddr_in);
185170620Sbms			psu->sin.sin_addr = *pina;
186170620Sbms		}
187170620Sbms	}
188170620Sbms
189170620Sbms	err = setsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup,
190170620Sbms	    sizeof(struct sockaddr_in), fmode, numsrc,
191170620Sbms	    (struct sockaddr_storage *)tmpslist);
192170620Sbms
193170620Sbms	if (tmpslist != NULL)
194170620Sbms		free(tmpslist);
195170620Sbms
196170620Sbms	return (err);
197170620Sbms#else /* !INET */
198170620Sbms	return (EAFNOSUPPORT);
199170620Sbms#endif /* INET */
200170620Sbms}
201170620Sbms
202170620Sbms/*
203170620Sbms * Get IPv4 source filter list in use on socket.
204170620Sbms *
205170620Sbms * Stubbed to getsourcefilter(). Performs conversion of structures which
206170620Sbms * may be inefficient; applications are encouraged to use the
207170620Sbms * protocol-independent API.
208170620Sbms * An slist of NULL may be used for guessing the required buffer size.
209170620Sbms */
210170620Sbmsint
211170620Sbmsgetipv4sourcefilter(int s, struct in_addr interface, struct in_addr group,
212170620Sbms    uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist)
213170620Sbms{
214170620Sbms	sockunion_t	*psu, *tmpslist;
215170620Sbms	sockunion_t	 tmpgroup;
216170620Sbms	struct in_addr	*pina;
217170620Sbms	int		 err;
218170620Sbms	size_t		 i;
219170620Sbms	uint32_t	 ifindex, onumsrc;
220170620Sbms
221170620Sbms	assert(s != -1);
222170620Sbms	assert(fmode != NULL);
223170620Sbms	assert(numsrc != NULL);
224170620Sbms
225170620Sbms	onumsrc = *numsrc;
226170620Sbms	*numsrc = 0;
227170620Sbms	tmpslist = NULL;
228170620Sbms
229170620Sbms	if (!IN_MULTICAST(ntohl(group.s_addr)) ||
230170620Sbms	    (onumsrc != 0 && slist == NULL)) {
231170620Sbms		errno = EINVAL;
232170620Sbms		return (-1);
233170620Sbms	}
234170620Sbms
235170620Sbms	ifindex = __inaddr_to_index(interface.s_addr);
236170620Sbms	if (ifindex == 0) {
237170620Sbms		errno = EADDRNOTAVAIL;
238170620Sbms		return (-1);
239170620Sbms	}
240170620Sbms
241170620Sbms	memset(&tmpgroup, 0, sizeof(sockunion_t));
242170620Sbms	tmpgroup.sin.sin_family = AF_INET;
243170620Sbms	tmpgroup.sin.sin_len = sizeof(struct sockaddr_in);
244170620Sbms	tmpgroup.sin.sin_addr = group;
245170620Sbms
246170620Sbms	if (onumsrc != 0 || slist != NULL) {
247170620Sbms		tmpslist = calloc(onumsrc, sizeof(sockunion_t));
248170620Sbms		if (tmpslist == NULL) {
249170620Sbms			errno = ENOMEM;
250170620Sbms			return (-1);
251170620Sbms		}
252170620Sbms	}
253170620Sbms
254170620Sbms	err = getsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup,
255170620Sbms	    sizeof(struct sockaddr_in), fmode, numsrc,
256170620Sbms	    (struct sockaddr_storage *)tmpslist);
257170620Sbms
258170620Sbms	if (tmpslist != NULL && *numsrc != 0) {
259170620Sbms		pina = slist;
260170620Sbms		psu = tmpslist;
261170620Sbms		for (i = 0; i < MIN(onumsrc, *numsrc); i++, psu++) {
262170620Sbms			if (psu->ss.ss_family != AF_INET)
263170620Sbms				continue;
264170620Sbms			*pina++ = psu->sin.sin_addr;
265170620Sbms		}
266170620Sbms		free(tmpslist);
267170620Sbms	}
268170620Sbms
269170620Sbms	return (err);
270170620Sbms}
271170620Sbms
272170620Sbms/*
273170620Sbms * Set protocol-independent source filter list in use on socket.
274170620Sbms */
275170620Sbmsint
276170620Sbmssetsourcefilter(int s, uint32_t interface, struct sockaddr *group,
277170620Sbms    socklen_t grouplen, uint32_t fmode, uint32_t numsrc,
278170620Sbms    struct sockaddr_storage *slist)
279170620Sbms{
280170620Sbms	struct __msfilterreq	 msfr;
281170620Sbms	sockunion_t		*psu;
282170620Sbms	int			 level, optname;
283170620Sbms
284170620Sbms	if (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE) {
285170620Sbms		errno = EINVAL;
286170620Sbms		return (-1);
287170620Sbms	}
288170620Sbms
289170620Sbms	psu = (sockunion_t *)group;
290170620Sbms	switch (psu->ss.ss_family) {
291170620Sbms#ifdef INET
292170620Sbms	case AF_INET:
293170620Sbms		if ((grouplen != sizeof(struct sockaddr_in) ||
294170620Sbms		    !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) {
295170620Sbms			errno = EINVAL;
296170620Sbms			return (-1);
297170620Sbms		}
298170620Sbms		level = IPPROTO_IP;
299170620Sbms		optname = IP_MSFILTER;
300170620Sbms		break;
301170620Sbms#endif
302170620Sbms#ifdef INET6
303170620Sbms	case AF_INET6:
304170620Sbms		if (grouplen != sizeof(struct sockaddr_in6) ||
305170625Sbms		    !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) {
306170620Sbms			errno = EINVAL;
307170620Sbms			return (-1);
308170620Sbms		}
309170620Sbms		level = IPPROTO_IPV6;
310170620Sbms		optname = IPV6_MSFILTER;
311170620Sbms		break;
312170620Sbms#endif
313170620Sbms	default:
314170620Sbms		errno = EAFNOSUPPORT;
315170620Sbms		return (-1);
316170620Sbms	}
317170620Sbms
318170620Sbms	memset(&msfr, 0, sizeof(msfr));
319170620Sbms	msfr.msfr_ifindex = interface;
320170620Sbms	msfr.msfr_fmode = fmode;
321170620Sbms	msfr.msfr_nsrcs = numsrc;
322170620Sbms	memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len);
323170620Sbms	msfr.msfr_srcs = slist;		/* pointer */
324170620Sbms
325171197Speter	return (_setsockopt(s, level, optname, &msfr, sizeof(msfr)));
326170620Sbms}
327170620Sbms
328170620Sbms/*
329170620Sbms * Get protocol-independent source filter list in use on socket.
330170620Sbms * An slist of NULL may be used for guessing the required buffer size.
331170620Sbms */
332170620Sbmsint
333170620Sbmsgetsourcefilter(int s, uint32_t interface, struct sockaddr *group,
334170620Sbms    socklen_t grouplen, uint32_t *fmode, uint32_t *numsrc,
335170620Sbms    struct sockaddr_storage *slist)
336170620Sbms{
337170620Sbms	struct __msfilterreq	 msfr;
338170620Sbms	sockunion_t		*psu;
339268878Spfg	socklen_t		 optlen;
340268867Spfg	int			 err, level, nsrcs, 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