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