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/types.h> 33170620Sbms#include <sys/param.h> 34170620Sbms#include <sys/ioctl.h> 35170620Sbms#include <sys/socket.h> 36170620Sbms 37170620Sbms#include <net/if_dl.h> 38170620Sbms#include <net/if.h> 39170620Sbms#include <netinet/in.h> 40170620Sbms#include <netinet/in_systm.h> 41170620Sbms#include <netinet/ip.h> 42170620Sbms#include <netinet/ip_var.h> 43170620Sbms 44170620Sbms#include <assert.h> 45170620Sbms#include <errno.h> 46170620Sbms#include <ifaddrs.h> 47170620Sbms#include <stdlib.h> 48170620Sbms#include <string.h> 49170620Sbms 50170620Sbms#include "un-namespace.h" 51170620Sbms 52170620Sbms/* 53170620Sbms * Advanced (Full-state) multicast group membership APIs [RFC3678] 54170620Sbms * Currently this module assumes IPv4 support (INET) in the base system. 55170620Sbms */ 56170620Sbms#ifndef INET 57170620Sbms#define INET 58170620Sbms#endif 59170620Sbms 60170620Sbmsunion sockunion { 61170620Sbms struct sockaddr_storage ss; 62170620Sbms struct sockaddr sa; 63170620Sbms struct sockaddr_dl sdl; 64170620Sbms#ifdef INET 65170620Sbms struct sockaddr_in sin; 66170620Sbms#endif 67170620Sbms#ifdef INET6 68170620Sbms struct sockaddr_in6 sin6; 69170620Sbms#endif 70170620Sbms}; 71170620Sbmstypedef union sockunion sockunion_t; 72170620Sbms 73170620Sbms#ifndef MIN 74170620Sbms#define MIN(a, b) ((a) < (b) ? (a) : (b)) 75170620Sbms#endif 76170620Sbms 77170620Sbms/* 78170620Sbms * Internal: Map an IPv4 unicast address to an interface index. 79170620Sbms * This is quite inefficient so it is recommended applications use 80170620Sbms * the newer, more portable, protocol independent API. 81170620Sbms */ 82170620Sbmsstatic uint32_t 83170620Sbms__inaddr_to_index(in_addr_t ifaddr) 84170620Sbms{ 85170620Sbms struct ifaddrs *ifa; 86170620Sbms struct ifaddrs *ifaddrs; 87170620Sbms char *ifname; 88170620Sbms int ifindex; 89170620Sbms sockunion_t *psu; 90170620Sbms 91170620Sbms if (getifaddrs(&ifaddrs) < 0) 92170620Sbms return (0); 93170620Sbms 94170620Sbms ifindex = 0; 95170620Sbms ifname = NULL; 96170620Sbms 97170620Sbms /* 98170620Sbms * Pass #1: Find the ifaddr entry corresponding to the 99170620Sbms * supplied IPv4 address. We should really use the ifindex 100170620Sbms * consistently for matches, however it is not available to 101170620Sbms * us on this pass. 102170620Sbms */ 103170620Sbms for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 104170620Sbms psu = (sockunion_t *)ifa->ifa_addr; 105170620Sbms if (psu && psu->ss.ss_family == AF_INET && 106170620Sbms psu->sin.sin_addr.s_addr == ifaddr) { 107170620Sbms ifname = ifa->ifa_name; 108170620Sbms break; 109170620Sbms } 110170620Sbms } 111170620Sbms if (ifname == NULL) 112170620Sbms goto out; 113170620Sbms 114170620Sbms /* 115170620Sbms * Pass #2: Find the index of the interface matching the name 116170620Sbms * we obtained from looking up the IPv4 ifaddr in pass #1. 117170620Sbms * There must be a better way of doing this. 118170620Sbms */ 119170620Sbms for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 120170620Sbms psu = (sockunion_t *)ifa->ifa_addr; 121170620Sbms if (psu && psu->ss.ss_family == AF_LINK && 122170620Sbms strcmp(ifa->ifa_name, ifname) == 0) { 123170620Sbms ifindex = psu->sdl.sdl_index; 124170620Sbms break; 125170620Sbms } 126170620Sbms } 127170620Sbms assert(ifindex != 0); 128170620Sbms 129170620Sbmsout: 130170620Sbms freeifaddrs(ifaddrs); 131170620Sbms return (ifindex); 132170620Sbms} 133170620Sbms 134170620Sbms/* 135170620Sbms * Set IPv4 source filter list in use on socket. 136170620Sbms * 137170620Sbms * Stubbed to setsourcefilter(). Performs conversion of structures which 138170620Sbms * may be inefficient; applications are encouraged to use the 139170620Sbms * protocol-independent API. 140170620Sbms */ 141170620Sbmsint 142170620Sbmssetipv4sourcefilter(int s, struct in_addr interface, struct in_addr group, 143170620Sbms uint32_t fmode, uint32_t numsrc, struct in_addr *slist) 144170620Sbms{ 145170620Sbms#ifdef INET 146170620Sbms sockunion_t tmpgroup; 147170620Sbms struct in_addr *pina; 148170620Sbms sockunion_t *psu, *tmpslist; 149170620Sbms int err; 150170620Sbms size_t i; 151170620Sbms uint32_t ifindex; 152170620Sbms 153170620Sbms assert(s != -1); 154170620Sbms 155170620Sbms tmpslist = NULL; 156170620Sbms 157170620Sbms if (!IN_MULTICAST(ntohl(group.s_addr)) || 158170620Sbms (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE)) { 159170620Sbms errno = EINVAL; 160170620Sbms return (-1); 161170620Sbms } 162170620Sbms 163170620Sbms ifindex = __inaddr_to_index(interface.s_addr); 164170620Sbms if (ifindex == 0) { 165170620Sbms errno = EADDRNOTAVAIL; 166170620Sbms return (-1); 167170620Sbms } 168170620Sbms 169170620Sbms memset(&tmpgroup, 0, sizeof(sockunion_t)); 170170620Sbms tmpgroup.sin.sin_family = AF_INET; 171170620Sbms tmpgroup.sin.sin_len = sizeof(struct sockaddr_in); 172170620Sbms tmpgroup.sin.sin_addr = group; 173170620Sbms 174170620Sbms if (numsrc != 0 || slist != NULL) { 175170620Sbms tmpslist = calloc(numsrc, sizeof(sockunion_t)); 176170620Sbms if (tmpslist == NULL) { 177170620Sbms errno = ENOMEM; 178170620Sbms return (-1); 179170620Sbms } 180170620Sbms 181170620Sbms pina = slist; 182170620Sbms psu = tmpslist; 183170620Sbms for (i = 0; i < numsrc; i++, pina++, psu++) { 184170620Sbms psu->sin.sin_family = AF_INET; 185170620Sbms psu->sin.sin_len = sizeof(struct sockaddr_in); 186170620Sbms psu->sin.sin_addr = *pina; 187170620Sbms } 188170620Sbms } 189170620Sbms 190170620Sbms err = setsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup, 191170620Sbms sizeof(struct sockaddr_in), fmode, numsrc, 192170620Sbms (struct sockaddr_storage *)tmpslist); 193170620Sbms 194170620Sbms if (tmpslist != NULL) 195170620Sbms free(tmpslist); 196170620Sbms 197170620Sbms return (err); 198170620Sbms#else /* !INET */ 199170620Sbms return (EAFNOSUPPORT); 200170620Sbms#endif /* INET */ 201170620Sbms} 202170620Sbms 203170620Sbms/* 204170620Sbms * Get IPv4 source filter list in use on socket. 205170620Sbms * 206170620Sbms * Stubbed to getsourcefilter(). Performs conversion of structures which 207170620Sbms * may be inefficient; applications are encouraged to use the 208170620Sbms * protocol-independent API. 209170620Sbms * An slist of NULL may be used for guessing the required buffer size. 210170620Sbms */ 211170620Sbmsint 212170620Sbmsgetipv4sourcefilter(int s, struct in_addr interface, struct in_addr group, 213170620Sbms uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist) 214170620Sbms{ 215170620Sbms sockunion_t *psu, *tmpslist; 216170620Sbms sockunion_t tmpgroup; 217170620Sbms struct in_addr *pina; 218170620Sbms int err; 219170620Sbms size_t i; 220170620Sbms uint32_t ifindex, onumsrc; 221170620Sbms 222170620Sbms assert(s != -1); 223170620Sbms assert(fmode != NULL); 224170620Sbms assert(numsrc != NULL); 225170620Sbms 226170620Sbms onumsrc = *numsrc; 227170620Sbms *numsrc = 0; 228170620Sbms tmpslist = NULL; 229170620Sbms 230170620Sbms if (!IN_MULTICAST(ntohl(group.s_addr)) || 231170620Sbms (onumsrc != 0 && slist == NULL)) { 232170620Sbms errno = EINVAL; 233170620Sbms return (-1); 234170620Sbms } 235170620Sbms 236170620Sbms ifindex = __inaddr_to_index(interface.s_addr); 237170620Sbms if (ifindex == 0) { 238170620Sbms errno = EADDRNOTAVAIL; 239170620Sbms return (-1); 240170620Sbms } 241170620Sbms 242170620Sbms memset(&tmpgroup, 0, sizeof(sockunion_t)); 243170620Sbms tmpgroup.sin.sin_family = AF_INET; 244170620Sbms tmpgroup.sin.sin_len = sizeof(struct sockaddr_in); 245170620Sbms tmpgroup.sin.sin_addr = group; 246170620Sbms 247170620Sbms if (onumsrc != 0 || slist != NULL) { 248170620Sbms tmpslist = calloc(onumsrc, sizeof(sockunion_t)); 249170620Sbms if (tmpslist == NULL) { 250170620Sbms errno = ENOMEM; 251170620Sbms return (-1); 252170620Sbms } 253170620Sbms } 254170620Sbms 255170620Sbms err = getsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup, 256170620Sbms sizeof(struct sockaddr_in), fmode, numsrc, 257170620Sbms (struct sockaddr_storage *)tmpslist); 258170620Sbms 259170620Sbms if (tmpslist != NULL && *numsrc != 0) { 260170620Sbms pina = slist; 261170620Sbms psu = tmpslist; 262170620Sbms for (i = 0; i < MIN(onumsrc, *numsrc); i++, psu++) { 263170620Sbms if (psu->ss.ss_family != AF_INET) 264170620Sbms continue; 265170620Sbms *pina++ = psu->sin.sin_addr; 266170620Sbms } 267170620Sbms free(tmpslist); 268170620Sbms } 269170620Sbms 270170620Sbms return (err); 271170620Sbms} 272170620Sbms 273170620Sbms/* 274170620Sbms * Set protocol-independent source filter list in use on socket. 275170620Sbms */ 276170620Sbmsint 277170620Sbmssetsourcefilter(int s, uint32_t interface, struct sockaddr *group, 278170620Sbms socklen_t grouplen, uint32_t fmode, uint32_t numsrc, 279170620Sbms struct sockaddr_storage *slist) 280170620Sbms{ 281170620Sbms struct __msfilterreq msfr; 282170620Sbms sockunion_t *psu; 283170620Sbms int level, optname; 284170620Sbms 285170620Sbms if (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE) { 286170620Sbms errno = EINVAL; 287170620Sbms return (-1); 288170620Sbms } 289170620Sbms 290170620Sbms psu = (sockunion_t *)group; 291170620Sbms switch (psu->ss.ss_family) { 292170620Sbms#ifdef INET 293170620Sbms case AF_INET: 294170620Sbms if ((grouplen != sizeof(struct sockaddr_in) || 295170620Sbms !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) { 296170620Sbms errno = EINVAL; 297170620Sbms return (-1); 298170620Sbms } 299170620Sbms level = IPPROTO_IP; 300170620Sbms optname = IP_MSFILTER; 301170620Sbms break; 302170620Sbms#endif 303170620Sbms#ifdef INET6 304170620Sbms case AF_INET6: 305170620Sbms if (grouplen != sizeof(struct sockaddr_in6) || 306170625Sbms !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) { 307170620Sbms errno = EINVAL; 308170620Sbms return (-1); 309170620Sbms } 310170620Sbms level = IPPROTO_IPV6; 311170620Sbms optname = IPV6_MSFILTER; 312170620Sbms break; 313170620Sbms#endif 314170620Sbms default: 315170620Sbms errno = EAFNOSUPPORT; 316170620Sbms return (-1); 317170620Sbms } 318170620Sbms 319170620Sbms memset(&msfr, 0, sizeof(msfr)); 320170620Sbms msfr.msfr_ifindex = interface; 321170620Sbms msfr.msfr_fmode = fmode; 322170620Sbms msfr.msfr_nsrcs = numsrc; 323170620Sbms memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len); 324170620Sbms msfr.msfr_srcs = slist; /* pointer */ 325170620Sbms 326171197Speter return (_setsockopt(s, level, optname, &msfr, sizeof(msfr))); 327170620Sbms} 328170620Sbms 329170620Sbms/* 330170620Sbms * Get protocol-independent source filter list in use on socket. 331170620Sbms * An slist of NULL may be used for guessing the required buffer size. 332170620Sbms */ 333170620Sbmsint 334170620Sbmsgetsourcefilter(int s, uint32_t interface, struct sockaddr *group, 335170620Sbms socklen_t grouplen, uint32_t *fmode, uint32_t *numsrc, 336170620Sbms struct sockaddr_storage *slist) 337170620Sbms{ 338170620Sbms struct __msfilterreq msfr; 339170620Sbms sockunion_t *psu; 340191654Sbms int err, level, nsrcs, optlen, 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