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 30/* 8120237: enable INET6 */ 31#define __APPLE_USE_RFC_3542 32 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 62/* 8120237: enable INET6 */ 63#ifndef INET6 64#define INET6 65#endif 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, optname; 348 unsigned int optlen; 349 350 if (interface == 0 || group == NULL || numsrc == NULL || 351 fmode == NULL) { 352 errno = EINVAL; 353 return (-1); 354 } 355 356 nsrcs = *numsrc; 357 *numsrc = 0; 358 *fmode = 0; 359 360 psu = (sockunion_t *)group; 361 switch (psu->ss.ss_family) { 362#ifdef INET 363 case AF_INET: 364 if ((grouplen != sizeof(struct sockaddr_in) || 365 !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) { 366 errno = EINVAL; 367 return (-1); 368 } 369 level = IPPROTO_IP; 370 optname = IP_MSFILTER; 371 break; 372#endif 373#ifdef INET6 374 case AF_INET6: 375 if (grouplen != sizeof(struct sockaddr_in6) || 376 !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) { 377 errno = EINVAL; 378 return (-1); 379 } 380 level = IPPROTO_IPV6; 381 optname = IPV6_MSFILTER; 382 break; 383#endif 384 default: 385 errno = EAFNOSUPPORT; 386 return (-1); 387 break; 388 } 389 390 optlen = sizeof(struct __msfilterreq); 391 memset(&msfr, 0, optlen); 392 msfr.msfr_ifindex = interface; 393 msfr.msfr_fmode = 0; 394 msfr.msfr_nsrcs = nsrcs; 395 memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len); 396 397 /* 398 * msfr_srcs is a pointer to a vector of sockaddr_storage. It 399 * may be NULL. The kernel will always return the total number 400 * of filter entries for the group in msfr.msfr_nsrcs. 401 */ 402 msfr.msfr_srcs = slist; 403 err = _getsockopt(s, level, optname, &msfr, &optlen); 404 if (err == 0) { 405 *numsrc = msfr.msfr_nsrcs; 406 *fmode = msfr.msfr_fmode; 407 } 408 409 return (err); 410} 411