1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apr_arch_networkio.h" 18#include "apr_network_io.h" 19#include "apr_support.h" 20#include "apr_portable.h" 21#include "apr_arch_inherit.h" 22 23#ifdef HAVE_GETIFADDRS 24#include <net/if.h> 25#include <ifaddrs.h> 26#endif 27 28#ifdef HAVE_STRUCT_IPMREQ 29static void fill_mip_v4(struct ip_mreq *mip, apr_sockaddr_t *mcast, 30 apr_sockaddr_t *iface) 31{ 32 mip->imr_multiaddr = mcast->sa.sin.sin_addr; 33 if (iface == NULL) { 34 mip->imr_interface.s_addr = INADDR_ANY; 35 } 36 else { 37 mip->imr_interface = iface->sa.sin.sin_addr; 38 } 39} 40 41/* This function is only interested in AF_INET6 sockets, so a noop 42 * "return 0" implementation for the !APR_HAVE_IPV6 build is 43 * sufficient. */ 44static unsigned int find_if_index(const apr_sockaddr_t *iface) 45{ 46 unsigned int index = 0; 47#if defined(HAVE_GETIFADDRS) && APR_HAVE_IPV6 48 struct ifaddrs *ifp, *ifs; 49 50 /** 51 * TODO: getifaddrs is only portable to *BSD and OS X. Using ioctl 52 * and SIOCGIFCONF is needed for Linux/Solaris support. 53 * 54 * There is a wrapper that takes the messy ioctl interface into 55 * getifaddrs. The license is acceptable, but, It is a fairly large 56 * chunk of code. 57 */ 58 if (getifaddrs(&ifs) != 0) { 59 return 0; 60 } 61 62 for (ifp = ifs; ifp; ifp = ifp->ifa_next) { 63 if (ifp->ifa_addr != NULL && ifp->ifa_addr->sa_family == AF_INET6) { 64 if (memcmp(&iface->sa.sin6.sin6_addr, 65 &((struct sockaddr_in6*)ifp->ifa_addr)->sin6_addr, 66 sizeof(iface->sa.sin6.sin6_addr)) == 0) { 67 index = if_nametoindex(ifp->ifa_name); 68 break; 69 } 70 } 71 } 72 73 freeifaddrs(ifs); 74#endif 75 return index; 76} 77 78#if APR_HAVE_IPV6 79static void fill_mip_v6(struct ipv6_mreq *mip, const apr_sockaddr_t *mcast, 80 const apr_sockaddr_t *iface) 81{ 82 memcpy(&mip->ipv6mr_multiaddr, mcast->ipaddr_ptr, 83 sizeof(mip->ipv6mr_multiaddr)); 84 85 if (iface == NULL) { 86 mip->ipv6mr_interface = 0; 87 } 88 else { 89 mip->ipv6mr_interface = find_if_index(iface); 90 } 91} 92 93#endif 94 95static int sock_is_ipv4(apr_socket_t *sock) 96{ 97 if (sock->local_addr->family == APR_INET) 98 return 1; 99 return 0; 100} 101 102#if APR_HAVE_IPV6 103static int sock_is_ipv6(apr_socket_t *sock) 104{ 105 if (sock->local_addr->family == APR_INET6) 106 return 1; 107 return 0; 108} 109#endif 110 111static apr_status_t do_mcast(int type, apr_socket_t *sock, 112 apr_sockaddr_t *mcast, apr_sockaddr_t *iface, 113 apr_sockaddr_t *source) 114{ 115 struct ip_mreq mip4; 116 apr_status_t rv = APR_SUCCESS; 117#if APR_HAVE_IPV6 118 struct ipv6_mreq mip6; 119#endif 120#ifdef GROUP_FILTER_SIZE 121 struct group_source_req mip; 122 int ip_proto; 123#endif 124 125 if (source != NULL) { 126#ifdef GROUP_FILTER_SIZE 127 if (sock_is_ipv4(sock)) { 128 ip_proto = IPPROTO_IP; 129 } 130#if APR_HAVE_IPV6 131 else if (sock_is_ipv6(sock)) { 132 ip_proto = IPPROTO_IPV6; 133 } 134#endif 135 else { 136 return APR_ENOTIMPL; 137 } 138 139 if (type == IP_ADD_MEMBERSHIP) 140 type = MCAST_JOIN_SOURCE_GROUP; 141 else if (type == IP_DROP_MEMBERSHIP) 142 type = MCAST_LEAVE_SOURCE_GROUP; 143 else 144 return APR_ENOTIMPL; 145 146 mip.gsr_interface = find_if_index(iface); 147 memcpy(&mip.gsr_group, mcast->ipaddr_ptr, sizeof(mip.gsr_group)); 148 memcpy(&mip.gsr_source, source->ipaddr_ptr, sizeof(mip.gsr_source)); 149 150 if (setsockopt(sock->socketdes, ip_proto, type, (const void *) &mip, 151 sizeof(mip)) == -1) { 152 rv = errno; 153 } 154#else 155 /* We do not support Source-Specific Multicast. */ 156 return APR_ENOTIMPL; 157#endif 158 } 159 else { 160 if (sock_is_ipv4(sock)) { 161 162 fill_mip_v4(&mip4, mcast, iface); 163 164 if (setsockopt(sock->socketdes, IPPROTO_IP, type, 165 (const void *) &mip4, sizeof(mip4)) == -1) { 166 rv = errno; 167 } 168 } 169#if APR_HAVE_IPV6 && defined(IPV6_JOIN_GROUP) && defined(IPV6_LEAVE_GROUP) 170 else if (sock_is_ipv6(sock)) { 171 if (type == IP_ADD_MEMBERSHIP) { 172 type = IPV6_JOIN_GROUP; 173 } 174 else if (type == IP_DROP_MEMBERSHIP) { 175 type = IPV6_LEAVE_GROUP; 176 } 177 else { 178 return APR_ENOTIMPL; 179 } 180 181 fill_mip_v6(&mip6, mcast, iface); 182 183 if (setsockopt(sock->socketdes, IPPROTO_IPV6, type, 184 (const void *) &mip6, sizeof(mip6)) == -1) { 185 rv = errno; 186 } 187 } 188#endif 189 else { 190 rv = APR_ENOTIMPL; 191 } 192 } 193 return rv; 194} 195 196/* Set the IP_MULTICAST_TTL or IP_MULTICAST_LOOP option, or IPv6 197 * equivalents, for the socket, to the given value. Note that this 198 * function *only works* for those particular option types. */ 199static apr_status_t do_mcast_opt(int type, apr_socket_t *sock, 200 apr_byte_t value) 201{ 202 apr_status_t rv = APR_SUCCESS; 203 204 if (sock_is_ipv4(sock)) { 205 /* For the IP_MULTICAST_* options, this must be a (char *) 206 * pointer. */ 207 if (setsockopt(sock->socketdes, IPPROTO_IP, type, 208 (const void *) &value, sizeof(value)) == -1) { 209 rv = errno; 210 } 211 } 212#if APR_HAVE_IPV6 213 else if (sock_is_ipv6(sock)) { 214 /* For the IPV6_* options, an (int *) pointer must be used. */ 215 int ivalue = value; 216 217 if (type == IP_MULTICAST_TTL) { 218 type = IPV6_MULTICAST_HOPS; 219 } 220 else if (type == IP_MULTICAST_LOOP) { 221 type = IPV6_MULTICAST_LOOP; 222 } 223 else { 224 return APR_ENOTIMPL; 225 } 226 227 if (setsockopt(sock->socketdes, IPPROTO_IPV6, type, 228 (const void *) &ivalue, sizeof(ivalue)) == -1) { 229 rv = errno; 230 } 231 } 232#endif 233 else { 234 rv = APR_ENOTIMPL; 235 } 236 237 return rv; 238} 239#endif 240 241APR_DECLARE(apr_status_t) apr_mcast_join(apr_socket_t *sock, 242 apr_sockaddr_t *join, 243 apr_sockaddr_t *iface, 244 apr_sockaddr_t *source) 245{ 246#if defined(IP_ADD_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ) 247 return do_mcast(IP_ADD_MEMBERSHIP, sock, join, iface, source); 248#else 249 return APR_ENOTIMPL; 250#endif 251} 252 253APR_DECLARE(apr_status_t) apr_mcast_leave(apr_socket_t *sock, 254 apr_sockaddr_t *addr, 255 apr_sockaddr_t *iface, 256 apr_sockaddr_t *source) 257{ 258#if defined(IP_DROP_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ) 259 return do_mcast(IP_DROP_MEMBERSHIP, sock, addr, iface, source); 260#else 261 return APR_ENOTIMPL; 262#endif 263} 264 265APR_DECLARE(apr_status_t) apr_mcast_hops(apr_socket_t *sock, apr_byte_t ttl) 266{ 267#if defined(IP_MULTICAST_TTL) && defined(HAVE_STRUCT_IPMREQ) 268 return do_mcast_opt(IP_MULTICAST_TTL, sock, ttl); 269#else 270 return APR_ENOTIMPL; 271#endif 272} 273 274APR_DECLARE(apr_status_t) apr_mcast_loopback(apr_socket_t *sock, 275 apr_byte_t opt) 276{ 277#if defined(IP_MULTICAST_LOOP) && defined(HAVE_STRUCT_IPMREQ) 278 return do_mcast_opt(IP_MULTICAST_LOOP, sock, opt); 279#else 280 return APR_ENOTIMPL; 281#endif 282} 283 284APR_DECLARE(apr_status_t) apr_mcast_interface(apr_socket_t *sock, 285 apr_sockaddr_t *iface) 286{ 287#if defined(IP_MULTICAST_IF) && defined(HAVE_STRUCT_IPMREQ) 288 apr_status_t rv = APR_SUCCESS; 289 290 if (sock_is_ipv4(sock)) { 291 if (setsockopt(sock->socketdes, IPPROTO_IP, IP_MULTICAST_IF, 292 (const void *) &iface->sa.sin.sin_addr, 293 sizeof(iface->sa.sin.sin_addr)) == -1) { 294 rv = errno; 295 } 296 } 297#if APR_HAVE_IPV6 298 else if (sock_is_ipv6(sock)) { 299 unsigned int idx = find_if_index(iface); 300 if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_MULTICAST_IF, 301 (const void *) &idx, sizeof(idx)) == -1) { 302 rv = errno; 303 } 304 } 305#endif 306 else { 307 rv = APR_ENOTIMPL; 308 } 309 return rv; 310#else 311 return APR_ENOTIMPL; 312#endif 313} 314