1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251875Speter * contributor license agreements. See the NOTICE file distributed with 3251875Speter * this work for additional information regarding copyright ownership. 4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251875Speter * (the "License"); you may not use this file except in compliance with 6251875Speter * the License. You may obtain a copy of the License at 7251875Speter * 8251875Speter * http://www.apache.org/licenses/LICENSE-2.0 9251875Speter * 10251875Speter * Unless required by applicable law or agreed to in writing, software 11251875Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251875Speter * See the License for the specific language governing permissions and 14251875Speter * limitations under the License. 15251875Speter */ 16251875Speter 17251875Speter#include "apr_arch_networkio.h" 18251875Speter#include "apr_network_io.h" 19251875Speter#include "apr_support.h" 20251875Speter#include "apr_portable.h" 21251875Speter#include "apr_arch_inherit.h" 22251875Speter 23251875Speter#ifdef HAVE_GETIFADDRS 24251875Speter#include <net/if.h> 25251875Speter#include <ifaddrs.h> 26251875Speter#endif 27251875Speter 28251875Speter#ifdef HAVE_STRUCT_IPMREQ 29251875Speterstatic void fill_mip_v4(struct ip_mreq *mip, apr_sockaddr_t *mcast, 30251875Speter apr_sockaddr_t *iface) 31251875Speter{ 32251875Speter mip->imr_multiaddr = mcast->sa.sin.sin_addr; 33251875Speter if (iface == NULL) { 34251875Speter mip->imr_interface.s_addr = INADDR_ANY; 35251875Speter } 36251875Speter else { 37251875Speter mip->imr_interface = iface->sa.sin.sin_addr; 38251875Speter } 39251875Speter} 40251875Speter 41251875Speter/* This function is only interested in AF_INET6 sockets, so a noop 42251875Speter * "return 0" implementation for the !APR_HAVE_IPV6 build is 43251875Speter * sufficient. */ 44251875Speterstatic unsigned int find_if_index(const apr_sockaddr_t *iface) 45251875Speter{ 46251875Speter unsigned int index = 0; 47251875Speter#if defined(HAVE_GETIFADDRS) && APR_HAVE_IPV6 48251875Speter struct ifaddrs *ifp, *ifs; 49251875Speter 50251875Speter /** 51251875Speter * TODO: getifaddrs is only portable to *BSD and OS X. Using ioctl 52251875Speter * and SIOCGIFCONF is needed for Linux/Solaris support. 53251875Speter * 54251875Speter * There is a wrapper that takes the messy ioctl interface into 55251875Speter * getifaddrs. The license is acceptable, but, It is a fairly large 56251875Speter * chunk of code. 57251875Speter */ 58251875Speter if (getifaddrs(&ifs) != 0) { 59251875Speter return 0; 60251875Speter } 61251875Speter 62251875Speter for (ifp = ifs; ifp; ifp = ifp->ifa_next) { 63251875Speter if (ifp->ifa_addr != NULL && ifp->ifa_addr->sa_family == AF_INET6) { 64251875Speter if (memcmp(&iface->sa.sin6.sin6_addr, 65251875Speter &ifp->ifa_addr->sa_data[0], 66251875Speter sizeof(iface->sa.sin6.sin6_addr)) == 0) { 67251875Speter index = if_nametoindex(ifp->ifa_name); 68251875Speter break; 69251875Speter } 70251875Speter } 71251875Speter } 72251875Speter 73251875Speter freeifaddrs(ifs); 74251875Speter#endif 75251875Speter return index; 76251875Speter} 77251875Speter 78251875Speter#if APR_HAVE_IPV6 79251875Speterstatic void fill_mip_v6(struct ipv6_mreq *mip, const apr_sockaddr_t *mcast, 80251875Speter const apr_sockaddr_t *iface) 81251875Speter{ 82251875Speter memcpy(&mip->ipv6mr_multiaddr, mcast->ipaddr_ptr, 83251875Speter sizeof(mip->ipv6mr_multiaddr)); 84251875Speter 85251875Speter if (iface == NULL) { 86251875Speter mip->ipv6mr_interface = 0; 87251875Speter } 88251875Speter else { 89251875Speter mip->ipv6mr_interface = find_if_index(iface); 90251875Speter } 91251875Speter} 92251875Speter 93251875Speter#endif 94251875Speter 95251875Speterstatic int sock_is_ipv4(apr_socket_t *sock) 96251875Speter{ 97251875Speter if (sock->local_addr->family == APR_INET) 98251875Speter return 1; 99251875Speter return 0; 100251875Speter} 101251875Speter 102251875Speter#if APR_HAVE_IPV6 103251875Speterstatic int sock_is_ipv6(apr_socket_t *sock) 104251875Speter{ 105251875Speter if (sock->local_addr->family == APR_INET6) 106251875Speter return 1; 107251875Speter return 0; 108251875Speter} 109251875Speter#endif 110251875Speter 111251875Speterstatic apr_status_t do_mcast(int type, apr_socket_t *sock, 112251875Speter apr_sockaddr_t *mcast, apr_sockaddr_t *iface, 113251875Speter apr_sockaddr_t *source) 114251875Speter{ 115251875Speter struct ip_mreq mip4; 116251875Speter apr_status_t rv = APR_SUCCESS; 117251875Speter#if APR_HAVE_IPV6 118251875Speter struct ipv6_mreq mip6; 119251875Speter#endif 120251875Speter#ifdef GROUP_FILTER_SIZE 121251875Speter struct group_source_req mip; 122251875Speter int ip_proto; 123251875Speter#endif 124251875Speter 125251875Speter if (source != NULL) { 126251875Speter#ifdef GROUP_FILTER_SIZE 127251875Speter if (sock_is_ipv4(sock)) { 128251875Speter ip_proto = IPPROTO_IP; 129251875Speter } 130251875Speter#if APR_HAVE_IPV6 131251875Speter else if (sock_is_ipv6(sock)) { 132251875Speter ip_proto = IPPROTO_IPV6; 133251875Speter } 134251875Speter#endif 135251875Speter else { 136251875Speter return APR_ENOTIMPL; 137251875Speter } 138251875Speter 139251875Speter if (type == IP_ADD_MEMBERSHIP) 140251875Speter type = MCAST_JOIN_SOURCE_GROUP; 141251875Speter else if (type == IP_DROP_MEMBERSHIP) 142251875Speter type = MCAST_LEAVE_SOURCE_GROUP; 143251875Speter else 144251875Speter return APR_ENOTIMPL; 145251875Speter 146251875Speter mip.gsr_interface = find_if_index(iface); 147251875Speter memcpy(&mip.gsr_group, mcast->ipaddr_ptr, sizeof(mip.gsr_group)); 148251875Speter memcpy(&mip.gsr_source, source->ipaddr_ptr, sizeof(mip.gsr_source)); 149251875Speter 150251875Speter if (setsockopt(sock->socketdes, ip_proto, type, (const void *) &mip, 151251875Speter sizeof(mip)) == -1) { 152251875Speter rv = errno; 153251875Speter } 154251875Speter#else 155251875Speter /* We do not support Source-Specific Multicast. */ 156251875Speter return APR_ENOTIMPL; 157251875Speter#endif 158251875Speter } 159251875Speter else { 160251875Speter if (sock_is_ipv4(sock)) { 161251875Speter 162251875Speter fill_mip_v4(&mip4, mcast, iface); 163251875Speter 164251875Speter if (setsockopt(sock->socketdes, IPPROTO_IP, type, 165251875Speter (const void *) &mip4, sizeof(mip4)) == -1) { 166251875Speter rv = errno; 167251875Speter } 168251875Speter } 169251875Speter#if APR_HAVE_IPV6 && defined(IPV6_JOIN_GROUP) && defined(IPV6_LEAVE_GROUP) 170251875Speter else if (sock_is_ipv6(sock)) { 171251875Speter if (type == IP_ADD_MEMBERSHIP) { 172251875Speter type = IPV6_JOIN_GROUP; 173251875Speter } 174251875Speter else if (type == IP_DROP_MEMBERSHIP) { 175251875Speter type = IPV6_LEAVE_GROUP; 176251875Speter } 177251875Speter else { 178251875Speter return APR_ENOTIMPL; 179251875Speter } 180251875Speter 181251875Speter fill_mip_v6(&mip6, mcast, iface); 182251875Speter 183251875Speter if (setsockopt(sock->socketdes, IPPROTO_IPV6, type, 184251875Speter (const void *) &mip6, sizeof(mip6)) == -1) { 185251875Speter rv = errno; 186251875Speter } 187251875Speter } 188251875Speter#endif 189251875Speter else { 190251875Speter rv = APR_ENOTIMPL; 191251875Speter } 192251875Speter } 193251875Speter return rv; 194251875Speter} 195251875Speter 196253734Speter/* Set the IP_MULTICAST_TTL or IP_MULTICAST_LOOP option, or IPv6 197253734Speter * equivalents, for the socket, to the given value. Note that this 198253734Speter * function *only works* for those particular option types. */ 199251875Speterstatic apr_status_t do_mcast_opt(int type, apr_socket_t *sock, 200251875Speter apr_byte_t value) 201251875Speter{ 202251875Speter apr_status_t rv = APR_SUCCESS; 203251875Speter 204251875Speter if (sock_is_ipv4(sock)) { 205253734Speter /* For the IP_MULTICAST_* options, this must be a (char *) 206253734Speter * pointer. */ 207251875Speter if (setsockopt(sock->socketdes, IPPROTO_IP, type, 208251875Speter (const void *) &value, sizeof(value)) == -1) { 209251875Speter rv = errno; 210251875Speter } 211251875Speter } 212251875Speter#if APR_HAVE_IPV6 213251875Speter else if (sock_is_ipv6(sock)) { 214253734Speter /* For the IPV6_* options, an (int *) pointer must be used. */ 215253734Speter int ivalue = value; 216253734Speter 217251875Speter if (type == IP_MULTICAST_TTL) { 218251875Speter type = IPV6_MULTICAST_HOPS; 219251875Speter } 220253734Speter else if (type == IP_MULTICAST_LOOP) { 221253734Speter type = IPV6_MULTICAST_LOOP; 222253734Speter } 223251875Speter else { 224251875Speter return APR_ENOTIMPL; 225251875Speter } 226251875Speter 227251875Speter if (setsockopt(sock->socketdes, IPPROTO_IPV6, type, 228253734Speter (const void *) &ivalue, sizeof(ivalue)) == -1) { 229251875Speter rv = errno; 230251875Speter } 231251875Speter } 232251875Speter#endif 233251875Speter else { 234251875Speter rv = APR_ENOTIMPL; 235251875Speter } 236251875Speter 237251875Speter return rv; 238251875Speter} 239251875Speter#endif 240251875Speter 241251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_join(apr_socket_t *sock, 242251875Speter apr_sockaddr_t *join, 243251875Speter apr_sockaddr_t *iface, 244251875Speter apr_sockaddr_t *source) 245251875Speter{ 246251875Speter#if defined(IP_ADD_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ) 247251875Speter return do_mcast(IP_ADD_MEMBERSHIP, sock, join, iface, source); 248251875Speter#else 249251875Speter return APR_ENOTIMPL; 250251875Speter#endif 251251875Speter} 252251875Speter 253251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_leave(apr_socket_t *sock, 254251875Speter apr_sockaddr_t *addr, 255251875Speter apr_sockaddr_t *iface, 256251875Speter apr_sockaddr_t *source) 257251875Speter{ 258251875Speter#if defined(IP_DROP_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ) 259251875Speter return do_mcast(IP_DROP_MEMBERSHIP, sock, addr, iface, source); 260251875Speter#else 261251875Speter return APR_ENOTIMPL; 262251875Speter#endif 263251875Speter} 264251875Speter 265251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_hops(apr_socket_t *sock, apr_byte_t ttl) 266251875Speter{ 267251875Speter#if defined(IP_MULTICAST_TTL) && defined(HAVE_STRUCT_IPMREQ) 268251875Speter return do_mcast_opt(IP_MULTICAST_TTL, sock, ttl); 269251875Speter#else 270251875Speter return APR_ENOTIMPL; 271251875Speter#endif 272251875Speter} 273251875Speter 274251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_loopback(apr_socket_t *sock, 275251875Speter apr_byte_t opt) 276251875Speter{ 277251875Speter#if defined(IP_MULTICAST_LOOP) && defined(HAVE_STRUCT_IPMREQ) 278251875Speter return do_mcast_opt(IP_MULTICAST_LOOP, sock, opt); 279251875Speter#else 280251875Speter return APR_ENOTIMPL; 281251875Speter#endif 282251875Speter} 283251875Speter 284251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_interface(apr_socket_t *sock, 285251875Speter apr_sockaddr_t *iface) 286251875Speter{ 287251875Speter#if defined(IP_MULTICAST_IF) && defined(HAVE_STRUCT_IPMREQ) 288251875Speter apr_status_t rv = APR_SUCCESS; 289251875Speter 290251875Speter if (sock_is_ipv4(sock)) { 291251875Speter if (setsockopt(sock->socketdes, IPPROTO_IP, IP_MULTICAST_IF, 292251875Speter (const void *) &iface->sa.sin.sin_addr, 293251875Speter sizeof(iface->sa.sin.sin_addr)) == -1) { 294251875Speter rv = errno; 295251875Speter } 296251875Speter } 297251875Speter#if APR_HAVE_IPV6 298251875Speter else if (sock_is_ipv6(sock)) { 299251875Speter unsigned int idx = find_if_index(iface); 300251875Speter if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_MULTICAST_IF, 301251875Speter (const void *) &idx, sizeof(idx)) == -1) { 302251875Speter rv = errno; 303251875Speter } 304251875Speter } 305251875Speter#endif 306251875Speter else { 307251875Speter rv = APR_ENOTIMPL; 308251875Speter } 309251875Speter return rv; 310251875Speter#else 311251875Speter return APR_ENOTIMPL; 312251875Speter#endif 313251875Speter} 314