]> git.saurik.com Git - apple/libc.git/blame - net/FreeBSD/sourcefilter.c
Libc-825.26.tar.gz
[apple/libc.git] / net / FreeBSD / sourcefilter.c
CommitLineData
1f2f436a
A
1/*-
2 * Copyright (c) 2007-2009 Bruce Simpson.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: src/lib/libc/net/sourcefilter.c,v 1.5 2009/04/29 09:58:31 bms Exp $");
29
ad3c9f2a
A
30/* 8120237: enable INET6 */
31#define __APPLE_USE_RFC_3542
32
1f2f436a
A
33#include "namespace.h"
34
35#include <sys/types.h>
36#include <sys/param.h>
37#include <sys/ioctl.h>
38#include <sys/socket.h>
39
40#include <net/if_dl.h>
41#include <net/if.h>
42#include <netinet/in.h>
43#include <netinet/in_systm.h>
44#include <netinet/ip.h>
45#include <netinet/ip_var.h>
46
47#include <assert.h>
48#include <errno.h>
49#include <ifaddrs.h>
50#include <stdlib.h>
51#include <string.h>
52
53#include "un-namespace.h"
54
55/*
56 * Advanced (Full-state) multicast group membership APIs [RFC3678]
57 * Currently this module assumes IPv4 support (INET) in the base system.
58 */
59#ifndef INET
60#define INET
61#endif
ad3c9f2a
A
62/* 8120237: enable INET6 */
63#ifndef INET6
64#define INET6
65#endif
1f2f436a
A
66
67union sockunion {
68 struct sockaddr_storage ss;
69 struct sockaddr sa;
70 struct sockaddr_dl sdl;
71#ifdef INET
72 struct sockaddr_in sin;
73#endif
74#ifdef INET6
75 struct sockaddr_in6 sin6;
76#endif
77};
78typedef union sockunion sockunion_t;
79
80#ifndef MIN
81#define MIN(a, b) ((a) < (b) ? (a) : (b))
82#endif
83
84/*
85 * Internal: Map an IPv4 unicast address to an interface index.
86 * This is quite inefficient so it is recommended applications use
87 * the newer, more portable, protocol independent API.
88 */
89static uint32_t
90__inaddr_to_index(in_addr_t ifaddr)
91{
92 struct ifaddrs *ifa;
93 struct ifaddrs *ifaddrs;
94 char *ifname;
95 int ifindex;
96 sockunion_t *psu;
97
98 if (getifaddrs(&ifaddrs) < 0)
99 return (0);
100
101 ifindex = 0;
102 ifname = NULL;
103
104 /*
105 * Pass #1: Find the ifaddr entry corresponding to the
106 * supplied IPv4 address. We should really use the ifindex
107 * consistently for matches, however it is not available to
108 * us on this pass.
109 */
110 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
111 psu = (sockunion_t *)ifa->ifa_addr;
112 if (psu && psu->ss.ss_family == AF_INET &&
113 psu->sin.sin_addr.s_addr == ifaddr) {
114 ifname = ifa->ifa_name;
115 break;
116 }
117 }
118 if (ifname == NULL)
119 goto out;
120
121 /*
122 * Pass #2: Find the index of the interface matching the name
123 * we obtained from looking up the IPv4 ifaddr in pass #1.
124 * There must be a better way of doing this.
125 */
126 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
127 psu = (sockunion_t *)ifa->ifa_addr;
128 if (psu && psu->ss.ss_family == AF_LINK &&
129 strcmp(ifa->ifa_name, ifname) == 0) {
130 ifindex = psu->sdl.sdl_index;
131 break;
132 }
133 }
134 assert(ifindex != 0);
135
136out:
137 freeifaddrs(ifaddrs);
138 return (ifindex);
139}
140
141/*
142 * Set IPv4 source filter list in use on socket.
143 *
144 * Stubbed to setsourcefilter(). Performs conversion of structures which
145 * may be inefficient; applications are encouraged to use the
146 * protocol-independent API.
147 */
148int
149setipv4sourcefilter(int s, struct in_addr interface, struct in_addr group,
150 uint32_t fmode, uint32_t numsrc, struct in_addr *slist)
151{
152#ifdef INET
153 sockunion_t tmpgroup;
154 struct in_addr *pina;
155 sockunion_t *psu, *tmpslist;
156 int err;
157 size_t i;
158 uint32_t ifindex;
159
160 assert(s != -1);
161
162 tmpslist = NULL;
163
164 if (!IN_MULTICAST(ntohl(group.s_addr)) ||
165 (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE)) {
166 errno = EINVAL;
167 return (-1);
168 }
169
170 ifindex = __inaddr_to_index(interface.s_addr);
171 if (ifindex == 0) {
172 errno = EADDRNOTAVAIL;
173 return (-1);
174 }
175
176 memset(&tmpgroup, 0, sizeof(sockunion_t));
177 tmpgroup.sin.sin_family = AF_INET;
178 tmpgroup.sin.sin_len = sizeof(struct sockaddr_in);
179 tmpgroup.sin.sin_addr = group;
180
181 if (numsrc != 0 || slist != NULL) {
182 tmpslist = calloc(numsrc, sizeof(sockunion_t));
183 if (tmpslist == NULL) {
184 errno = ENOMEM;
185 return (-1);
186 }
187
188 pina = slist;
189 psu = tmpslist;
190 for (i = 0; i < numsrc; i++, pina++, psu++) {
191 psu->sin.sin_family = AF_INET;
192 psu->sin.sin_len = sizeof(struct sockaddr_in);
193 psu->sin.sin_addr = *pina;
194 }
195 }
196
197 err = setsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup,
198 sizeof(struct sockaddr_in), fmode, numsrc,
199 (struct sockaddr_storage *)tmpslist);
200
201 if (tmpslist != NULL)
202 free(tmpslist);
203
204 return (err);
205#else /* !INET */
206 return (EAFNOSUPPORT);
207#endif /* INET */
208}
209
210/*
211 * Get IPv4 source filter list in use on socket.
212 *
213 * Stubbed to getsourcefilter(). Performs conversion of structures which
214 * may be inefficient; applications are encouraged to use the
215 * protocol-independent API.
216 * An slist of NULL may be used for guessing the required buffer size.
217 */
218int
219getipv4sourcefilter(int s, struct in_addr interface, struct in_addr group,
220 uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist)
221{
222 sockunion_t *psu, *tmpslist;
223 sockunion_t tmpgroup;
224 struct in_addr *pina;
225 int err;
226 size_t i;
227 uint32_t ifindex, onumsrc;
228
229 assert(s != -1);
230 assert(fmode != NULL);
231 assert(numsrc != NULL);
232
233 onumsrc = *numsrc;
234 *numsrc = 0;
235 tmpslist = NULL;
236
237 if (!IN_MULTICAST(ntohl(group.s_addr)) ||
238 (onumsrc != 0 && slist == NULL)) {
239 errno = EINVAL;
240 return (-1);
241 }
242
243 ifindex = __inaddr_to_index(interface.s_addr);
244 if (ifindex == 0) {
245 errno = EADDRNOTAVAIL;
246 return (-1);
247 }
248
249 memset(&tmpgroup, 0, sizeof(sockunion_t));
250 tmpgroup.sin.sin_family = AF_INET;
251 tmpgroup.sin.sin_len = sizeof(struct sockaddr_in);
252 tmpgroup.sin.sin_addr = group;
253
254 if (onumsrc != 0 || slist != NULL) {
255 tmpslist = calloc(onumsrc, sizeof(sockunion_t));
256 if (tmpslist == NULL) {
257 errno = ENOMEM;
258 return (-1);
259 }
260 }
261
262 err = getsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup,
263 sizeof(struct sockaddr_in), fmode, numsrc,
264 (struct sockaddr_storage *)tmpslist);
265
266 if (tmpslist != NULL && *numsrc != 0) {
267 pina = slist;
268 psu = tmpslist;
269 for (i = 0; i < MIN(onumsrc, *numsrc); i++, psu++) {
270 if (psu->ss.ss_family != AF_INET)
271 continue;
272 *pina++ = psu->sin.sin_addr;
273 }
274 free(tmpslist);
275 }
276
277 return (err);
278}
279
280/*
281 * Set protocol-independent source filter list in use on socket.
282 */
283int
284setsourcefilter(int s, uint32_t interface, struct sockaddr *group,
285 socklen_t grouplen, uint32_t fmode, uint32_t numsrc,
286 struct sockaddr_storage *slist)
287{
288 struct __msfilterreq msfr;
289 sockunion_t *psu;
290 int level, optname;
291
292 if (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE) {
293 errno = EINVAL;
294 return (-1);
295 }
296
297 psu = (sockunion_t *)group;
298 switch (psu->ss.ss_family) {
299#ifdef INET
300 case AF_INET:
301 if ((grouplen != sizeof(struct sockaddr_in) ||
302 !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) {
303 errno = EINVAL;
304 return (-1);
305 }
306 level = IPPROTO_IP;
307 optname = IP_MSFILTER;
308 break;
309#endif
310#ifdef INET6
311 case AF_INET6:
312 if (grouplen != sizeof(struct sockaddr_in6) ||
313 !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) {
314 errno = EINVAL;
315 return (-1);
316 }
317 level = IPPROTO_IPV6;
318 optname = IPV6_MSFILTER;
319 break;
320#endif
321 default:
322 errno = EAFNOSUPPORT;
323 return (-1);
324 }
325
326 memset(&msfr, 0, sizeof(msfr));
327 msfr.msfr_ifindex = interface;
328 msfr.msfr_fmode = fmode;
329 msfr.msfr_nsrcs = numsrc;
330 memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len);
331 msfr.msfr_srcs = slist; /* pointer */
332
333 return (_setsockopt(s, level, optname, &msfr, sizeof(msfr)));
334}
335
336/*
337 * Get protocol-independent source filter list in use on socket.
338 * An slist of NULL may be used for guessing the required buffer size.
339 */
340int
341getsourcefilter(int s, uint32_t interface, struct sockaddr *group,
342 socklen_t grouplen, uint32_t *fmode, uint32_t *numsrc,
343 struct sockaddr_storage *slist)
344{
345 struct __msfilterreq msfr;
346 sockunion_t *psu;
347 int err, level, nsrcs, optlen, optname;
348
349 if (interface == 0 || group == NULL || numsrc == NULL ||
350 fmode == NULL) {
351 errno = EINVAL;
352 return (-1);
353 }
354
355 nsrcs = *numsrc;
356 *numsrc = 0;
357 *fmode = 0;
358
359 psu = (sockunion_t *)group;
360 switch (psu->ss.ss_family) {
361#ifdef INET
362 case AF_INET:
363 if ((grouplen != sizeof(struct sockaddr_in) ||
364 !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) {
365 errno = EINVAL;
366 return (-1);
367 }
368 level = IPPROTO_IP;
369 optname = IP_MSFILTER;
370 break;
371#endif
372#ifdef INET6
373 case AF_INET6:
374 if (grouplen != sizeof(struct sockaddr_in6) ||
375 !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) {
376 errno = EINVAL;
377 return (-1);
378 }
379 level = IPPROTO_IPV6;
380 optname = IPV6_MSFILTER;
381 break;
382#endif
383 default:
384 errno = EAFNOSUPPORT;
385 return (-1);
386 break;
387 }
388
389 optlen = sizeof(struct __msfilterreq);
390 memset(&msfr, 0, optlen);
391 msfr.msfr_ifindex = interface;
392 msfr.msfr_fmode = 0;
393 msfr.msfr_nsrcs = nsrcs;
394 memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len);
395
396 /*
397 * msfr_srcs is a pointer to a vector of sockaddr_storage. It
398 * may be NULL. The kernel will always return the total number
399 * of filter entries for the group in msfr.msfr_nsrcs.
400 */
401 msfr.msfr_srcs = slist;
402 err = _getsockopt(s, level, optname, &msfr, &optlen);
403 if (err == 0) {
404 *numsrc = msfr.msfr_nsrcs;
405 *fmode = msfr.msfr_fmode;
406 }
407
408 return (err);
409}