]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/apr/network_io/unix/multicast.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / apr / network_io / unix / multicast.c
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
29 static 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. */
44 static 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                        &ifp->ifa_addr->sa_data[0],
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
79 static 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
95 static 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
103 static 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
111 static 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. */
199 static 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
241 APR_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
253 APR_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
265 APR_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
274 APR_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
284 APR_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 }