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