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