1 /*****************************************************************************
2 * ipv6.c: IPv6 network abstraction layer
3 *****************************************************************************
4 * Copyright (C) 2002-2005 the VideoLAN team
7 * Authors: Alexis Guillard <alexis.guillard@bt.com>
8 * Christophe Massiot <massiot@via.ecp.fr>
9 * Remco Poortinga <poortinga@telin.nl>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
30 #include <sys/types.h>
40 #elif defined( _MSC_VER ) && defined( _WIN32 )
45 # include <winsock2.h>
46 # include <ws2tcpip.h>
47 #elif !defined( SYS_BEOS ) && !defined( SYS_NTO )
48 # include <netdb.h> /* hostent ... */
49 # include <sys/socket.h>
50 # include <netinet/in.h>
56 static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
57 /* the following will have to be removed when w32api defines them */
59 # define IPPROTO_IPV6 41
61 # ifndef IPV6_JOIN_GROUP
62 # define IPV6_JOIN_GROUP 12
64 # ifndef IPV6_MULTICAST_HOPS
65 # define IPV6_MULTICAST_HOPS 10
67 # ifndef IPV6_UNICAST_HOPS
68 # define IPV6_UNICAST_HOPS 4
70 # define close closesocket
73 #ifndef MCAST_JOIN_SOURCE_GROUP
74 # define MCAST_JOIN_SOURCE_GROUP 46
75 struct group_source_req
77 uint32_t gsr_interface; /* interface index */
78 struct sockaddr_storage gsr_group; /* group address */
79 struct sockaddr_storage gsr_source; /* source address */
83 /*****************************************************************************
85 *****************************************************************************/
86 static int OpenUDP( vlc_object_t * );
88 /*****************************************************************************
90 *****************************************************************************/
92 set_description( _("IPv6 network abstraction layer") );
93 set_capability( "network", 40 );
94 set_callbacks( OpenUDP, NULL );
97 /*****************************************************************************
98 * BuildAddr: utility function to build a struct sockaddr_in6
99 *****************************************************************************/
100 static int BuildAddr( vlc_object_t *p_this, struct sockaddr_in6 *p_socket,
101 const char *psz_address, int i_port )
103 struct addrinfo hints, *res;
106 memset( &hints, 0, sizeof( hints ) );
107 hints.ai_family = AF_INET6;
108 hints.ai_socktype = SOCK_DGRAM;
109 hints.ai_flags = AI_PASSIVE;
111 i = vlc_getaddrinfo( p_this, psz_address, 0, &hints, &res );
114 msg_Dbg( p_this, "%s: %s", psz_address, vlc_gai_strerror( i ) );
117 if ( res->ai_addrlen > sizeof (struct sockaddr_in6) )
119 vlc_freeaddrinfo( res );
123 memcpy( p_socket, res->ai_addr, res->ai_addrlen );
124 vlc_freeaddrinfo( res );
125 p_socket->sin6_port = htons( i_port );
130 /*****************************************************************************
131 * OpenUDP: open a UDP socket
132 *****************************************************************************
133 * psz_bind_addr, i_bind_port : address and port used for the bind()
134 * system call. If psz_bind_addr == NULL, the socket is bound to
135 * in6addr_any and broadcast reception is enabled. If i_bind_port == 0,
136 * 1234 is used. If psz_bind_addr is a multicast (class D) address,
137 * join the multicast group.
138 * psz_server_addr, i_server_port : address and port used for the connect()
139 * system call. It can avoid receiving packets from unauthorized IPs.
140 * Its use leads to great confusion and is currently discouraged.
141 * This function returns -1 in case of error.
142 *****************************************************************************/
143 static int OpenUDP( vlc_object_t * p_this )
145 network_socket_t *p_socket = p_this->p_private;
146 const char *psz_bind_addr = p_socket->psz_bind_addr;
147 int i_bind_port = p_socket->i_bind_port;
148 const char *psz_server_addr = p_socket->psz_server_addr;
149 int i_server_port = p_socket->i_server_port;
151 struct sockaddr_in6 sock;
154 /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
156 if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
158 msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
163 # ifdef IPV6_PROTECTION_LEVEL
164 if( ptr->ai_family == AF_INET6 )
166 i_val = PROTECTION_LEVEL_UNRESTRICTED;
167 setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
171 # warning You are using outdated headers for Winsock !
175 /* We may want to reuse an already used socket */
177 if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
178 (void *) &i_opt, sizeof( i_opt ) ) == -1 )
180 msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
186 /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
187 * packet loss caused by scheduling problems */
189 if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
190 (void *) &i_opt, sizeof( i_opt ) ) == -1 )
192 msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
196 /* Build the local socket */
197 if ( BuildAddr( p_this, &sock, psz_bind_addr, i_bind_port ) == -1 )
204 /* Under Win32 and for multicasting, we bind to IN6ADDR_ANY */
205 if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
207 struct sockaddr_in6 sockany = sock;
208 sockany.sin6_addr = in6addr_any;
209 sockany.sin6_scope_id = 0;
212 if( bind( i_handle, (struct sockaddr *)&sockany, sizeof( sock ) ) < 0 )
214 msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
222 if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
224 msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
229 /* Allow broadcast reception if we bound on in6addr_any */
230 if( !*psz_bind_addr )
233 if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
234 (void*) &i_opt, sizeof( i_opt ) ) == -1 )
236 msg_Warn( p_this, "IPv6 warning: cannot configure socket "
237 "(SO_BROADCAST: %s)", strerror(errno) );
241 /* Join the multicast group if the socket is a multicast address */
242 #if defined( WIN32 ) || defined( HAVE_IF_NAMETOINDEX )
243 if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
247 struct group_source_req imr;
248 struct sockaddr_in6 *p_sin6;
250 imr.gsr_interface = 0;
251 imr.gsr_group.ss_family = AF_INET6;
252 imr.gsr_source.ss_family = AF_INET6;
253 p_sin6 = (struct sockaddr_in6 *)&imr.gsr_group;
254 p_sin6->sin6_addr = sock.sin6_addr;
256 /* Build socket for remote connection */
257 msg_Dbg( p_this, "psz_server_addr : %s", psz_server_addr);
259 if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) )
261 msg_Warn( p_this, "cannot build remote address" );
265 p_sin6 = (struct sockaddr_in6 *)&imr.gsr_source;
266 p_sin6->sin6_addr = sock.sin6_addr;
268 msg_Dbg( p_this, "IPV6_ADD_SOURCE_MEMBERSHIP multicast request" );
269 if( setsockopt( i_handle, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP,
270 (char *)&imr, sizeof(struct group_source_req) ) == -1 )
273 msg_Err( p_this, "failed to join IP multicast group (%s)",
280 struct ipv6_mreq imr;
283 imr.ipv6mr_interface = sock.sin6_scope_id;
284 imr.ipv6mr_multiaddr = sock.sin6_addr;
285 res = setsockopt(i_handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void*) &imr,
287 sizeof(imr) + 4); /* Doesn't work without this */
294 msg_Err( p_this, "cannot join multicast group" );
299 msg_Warn( p_this, "Multicast IPv6 is not supported on your OS" );
303 if( *psz_server_addr )
305 int ttl = p_socket->i_ttl;
308 ttl = config_GetInt( p_this, "ttl" );
310 if( ttl < 1 ) ttl = 1;
312 /* Build socket for remote connection */
313 if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
315 msg_Warn( p_this, "cannot build remote address" );
320 /* Connect the socket */
321 if( connect( i_handle, (struct sockaddr *) &sock,
322 sizeof( sock ) ) == (-1) )
324 msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
329 /* Set the time-to-live */
332 #if defined( WIN32 ) || defined( HAVE_IF_NAMETOINDEX )
333 if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
335 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
336 (void *)&ttl, sizeof( ttl ) ) < 0 )
338 msg_Err( p_this, "failed to set multicast ttl (%s)",
345 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
346 (void *)&ttl, sizeof( ttl ) ) < 0 )
348 msg_Err( p_this, "failed to set unicast ttl (%s)",
355 p_socket->i_handle = i_handle;
357 var_Create( p_this, "mtu", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
358 var_Get( p_this, "mtu", &val );
359 p_socket->i_mtu = val.i_int;