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