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$"); 29 30#include "namespace.h" 31 32#include <sys/types.h> 33#include <sys/param.h> 34#include <sys/ioctl.h> 35#include <sys/socket.h> 36 37#include <net/if_dl.h> 38#include <net/if.h> 39#include <netinet/in.h> 40#include <netinet/in_systm.h> 41#include <netinet/ip.h> 42#include <netinet/ip_var.h> 43 44#include <assert.h> 45#include <errno.h> 46#include <ifaddrs.h> 47#include <stdlib.h> 48#include <string.h> 49 50#include "un-namespace.h" 51 52/* 53 * Advanced (Full-state) multicast group membership APIs [RFC3678] 54 * Currently this module assumes IPv4 support (INET) in the base system. 55 */ 56#ifndef INET 57#define INET 58#endif 59 60union sockunion { 61 struct sockaddr_storage ss; 62 struct sockaddr sa; 63 struct sockaddr_dl sdl; 64#ifdef INET 65 struct sockaddr_in sin; 66#endif 67#ifdef INET6 68 struct sockaddr_in6 sin6; 69#endif 70}; 71typedef union sockunion sockunion_t; 72 73#ifndef MIN 74#define MIN(a, b) ((a) < (b) ? (a) : (b)) 75#endif 76 77/* 78 * Internal: Map an IPv4 unicast address to an interface index. 79 * This is quite inefficient so it is recommended applications use 80 * the newer, more portable, protocol independent API. 81 */ 82static uint32_t 83__inaddr_to_index(in_addr_t ifaddr) 84{ 85 struct ifaddrs *ifa; 86 struct ifaddrs *ifaddrs; 87 char *ifname; 88 int ifindex; 89 sockunion_t *psu; 90 91 if (getifaddrs(&ifaddrs) < 0) 92 return (0); 93 94 ifindex = 0; 95 ifname = NULL; 96 97 /* 98 * Pass #1: Find the ifaddr entry corresponding to the 99 * supplied IPv4 address. We should really use the ifindex 100 * consistently for matches, however it is not available to 101 * us on this pass. 102 */ 103 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 104 psu = (sockunion_t *)ifa->ifa_addr; 105 if (psu && psu->ss.ss_family == AF_INET && 106 psu->sin.sin_addr.s_addr == ifaddr) { 107 ifname = ifa->ifa_name; 108 break; 109 } 110 } 111 if (ifname == NULL) 112 goto out; 113 114 /* 115 * Pass #2: Find the index of the interface matching the name 116 * we obtained from looking up the IPv4 ifaddr in pass #1. 117 * There must be a better way of doing this. 118 */ 119 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 120 psu = (sockunion_t *)ifa->ifa_addr; 121 if (psu && psu->ss.ss_family == AF_LINK && 122 strcmp(ifa->ifa_name, ifname) == 0) { 123 ifindex = LLINDEX(&psu->sdl); 124 break; 125 } 126 } 127 assert(ifindex != 0); 128 129out: 130 freeifaddrs(ifaddrs); 131 return (ifindex); 132} 133 134/* 135 * Set IPv4 source filter list in use on socket. 136 * 137 * Stubbed to setsourcefilter(). Performs conversion of structures which 138 * may be inefficient; applications are encouraged to use the 139 * protocol-independent API. 140 */ 141int 142setipv4sourcefilter(int s, struct in_addr interface, struct in_addr group, 143 uint32_t fmode, uint32_t numsrc, struct in_addr *slist) 144{ 145#ifdef INET 146 sockunion_t tmpgroup; 147 struct in_addr *pina; 148 sockunion_t *psu, *tmpslist; 149 int err; 150 size_t i; 151 uint32_t ifindex; 152 153 assert(s != -1); 154 155 tmpslist = NULL; 156 157 if (!IN_MULTICAST(ntohl(group.s_addr)) || 158 (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE)) { 159 errno = EINVAL; 160 return (-1); 161 } 162 163 ifindex = __inaddr_to_index(interface.s_addr); 164 if (ifindex == 0) { 165 errno = EADDRNOTAVAIL; 166 return (-1); 167 } 168 169 memset(&tmpgroup, 0, sizeof(sockunion_t)); 170 tmpgroup.sin.sin_family = AF_INET; 171 tmpgroup.sin.sin_len = sizeof(struct sockaddr_in); 172 tmpgroup.sin.sin_addr = group; 173 174 if (numsrc != 0 || slist != NULL) { 175 tmpslist = calloc(numsrc, sizeof(sockunion_t)); 176 if (tmpslist == NULL) { 177 errno = ENOMEM; 178 return (-1); 179 } 180 181 pina = slist; 182 psu = tmpslist; 183 for (i = 0; i < numsrc; i++, pina++, psu++) { 184 psu->sin.sin_family = AF_INET; 185 psu->sin.sin_len = sizeof(struct sockaddr_in); 186 psu->sin.sin_addr = *pina; 187 } 188 } 189 190 err = setsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup, 191 sizeof(struct sockaddr_in), fmode, numsrc, 192 (struct sockaddr_storage *)tmpslist); 193 194 if (tmpslist != NULL) 195 free(tmpslist); 196 197 return (err); 198#else /* !INET */ 199 return (EAFNOSUPPORT); 200#endif /* INET */ 201} 202 203/* 204 * Get IPv4 source filter list in use on socket. 205 * 206 * Stubbed to getsourcefilter(). Performs conversion of structures which 207 * may be inefficient; applications are encouraged to use the 208 * protocol-independent API. 209 * An slist of NULL may be used for guessing the required buffer size. 210 */ 211int 212getipv4sourcefilter(int s, struct in_addr interface, struct in_addr group, 213 uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist) 214{ 215 sockunion_t *psu, *tmpslist; 216 sockunion_t tmpgroup; 217 struct in_addr *pina; 218 int err; 219 size_t i; 220 uint32_t ifindex, onumsrc; 221 222 assert(s != -1); 223 assert(fmode != NULL); 224 assert(numsrc != NULL); 225 226 onumsrc = *numsrc; 227 *numsrc = 0; 228 tmpslist = NULL; 229 230 if (!IN_MULTICAST(ntohl(group.s_addr)) || 231 (onumsrc != 0 && slist == NULL)) { 232 errno = EINVAL; 233 return (-1); 234 } 235 236 ifindex = __inaddr_to_index(interface.s_addr); 237 if (ifindex == 0) { 238 errno = EADDRNOTAVAIL; 239 return (-1); 240 } 241 242 memset(&tmpgroup, 0, sizeof(sockunion_t)); 243 tmpgroup.sin.sin_family = AF_INET; 244 tmpgroup.sin.sin_len = sizeof(struct sockaddr_in); 245 tmpgroup.sin.sin_addr = group; 246 247 if (onumsrc != 0 || slist != NULL) { 248 tmpslist = calloc(onumsrc, sizeof(sockunion_t)); 249 if (tmpslist == NULL) { 250 errno = ENOMEM; 251 return (-1); 252 } 253 } 254 255 err = getsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup, 256 sizeof(struct sockaddr_in), fmode, numsrc, 257 (struct sockaddr_storage *)tmpslist); 258 259 if (tmpslist != NULL && *numsrc != 0) { 260 pina = slist; 261 psu = tmpslist; 262 for (i = 0; i < MIN(onumsrc, *numsrc); i++, psu++) { 263 if (psu->ss.ss_family != AF_INET) 264 continue; 265 *pina++ = psu->sin.sin_addr; 266 } 267 free(tmpslist); 268 } 269 270 return (err); 271} 272 273/* 274 * Set protocol-independent source filter list in use on socket. 275 */ 276int 277setsourcefilter(int s, uint32_t interface, struct sockaddr *group, 278 socklen_t grouplen, uint32_t fmode, uint32_t numsrc, 279 struct sockaddr_storage *slist) 280{ 281 struct __msfilterreq msfr; 282 sockunion_t *psu; 283 int level, optname; 284 285 if (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE) { 286 errno = EINVAL; 287 return (-1); 288 } 289 290 psu = (sockunion_t *)group; 291 switch (psu->ss.ss_family) { 292#ifdef INET 293 case AF_INET: 294 if ((grouplen != sizeof(struct sockaddr_in) || 295 !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) { 296 errno = EINVAL; 297 return (-1); 298 } 299 level = IPPROTO_IP; 300 optname = IP_MSFILTER; 301 break; 302#endif 303#ifdef INET6 304 case AF_INET6: 305 if (grouplen != sizeof(struct sockaddr_in6) || 306 !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) { 307 errno = EINVAL; 308 return (-1); 309 } 310 level = IPPROTO_IPV6; 311 optname = IPV6_MSFILTER; 312 break; 313#endif 314 default: 315 errno = EAFNOSUPPORT; 316 return (-1); 317 } 318 319 memset(&msfr, 0, sizeof(msfr)); 320 msfr.msfr_ifindex = interface; 321 msfr.msfr_fmode = fmode; 322 msfr.msfr_nsrcs = numsrc; 323 memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len); 324 msfr.msfr_srcs = slist; /* pointer */ 325 326 return (_setsockopt(s, level, optname, &msfr, sizeof(msfr))); 327} 328 329/* 330 * Get protocol-independent source filter list in use on socket. 331 * An slist of NULL may be used for guessing the required buffer size. 332 */ 333int 334getsourcefilter(int s, uint32_t interface, struct sockaddr *group, 335 socklen_t grouplen, uint32_t *fmode, uint32_t *numsrc, 336 struct sockaddr_storage *slist) 337{ 338 struct __msfilterreq msfr; 339 sockunion_t *psu; 340 socklen_t optlen; 341 int err, level, nsrcs, optname; 342 343 if (interface == 0 || group == NULL || numsrc == NULL || 344 fmode == NULL) { 345 errno = EINVAL; 346 return (-1); 347 } 348 349 nsrcs = *numsrc; 350 *numsrc = 0; 351 *fmode = 0; 352 353 psu = (sockunion_t *)group; 354 switch (psu->ss.ss_family) { 355#ifdef INET 356 case AF_INET: 357 if ((grouplen != sizeof(struct sockaddr_in) || 358 !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) { 359 errno = EINVAL; 360 return (-1); 361 } 362 level = IPPROTO_IP; 363 optname = IP_MSFILTER; 364 break; 365#endif 366#ifdef INET6 367 case AF_INET6: 368 if (grouplen != sizeof(struct sockaddr_in6) || 369 !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) { 370 errno = EINVAL; 371 return (-1); 372 } 373 level = IPPROTO_IPV6; 374 optname = IPV6_MSFILTER; 375 break; 376#endif 377 default: 378 errno = EAFNOSUPPORT; 379 return (-1); 380 break; 381 } 382 383 optlen = sizeof(struct __msfilterreq); 384 memset(&msfr, 0, optlen); 385 msfr.msfr_ifindex = interface; 386 msfr.msfr_fmode = 0; 387 msfr.msfr_nsrcs = nsrcs; 388 memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len); 389 390 /* 391 * msfr_srcs is a pointer to a vector of sockaddr_storage. It 392 * may be NULL. The kernel will always return the total number 393 * of filter entries for the group in msfr.msfr_nsrcs. 394 */ 395 msfr.msfr_srcs = slist; 396 err = _getsockopt(s, level, optname, &msfr, &optlen); 397 if (err == 0) { 398 *numsrc = msfr.msfr_nsrcs; 399 *fmode = msfr.msfr_fmode; 400 } 401 402 return (err); 403} 404