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