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