1 /*****************************************************************************
2 * ipv6.c: IPv6 network abstraction layer
3 *****************************************************************************
4 * Copyright (C) 2002-2006 the VideoLAN team
7 * Authors: Alexis Guillard <alexis.guillard@bt.com>
8 * Christophe Massiot <massiot@via.ecp.fr>
9 * Remco Poortinga <poortinga@telin.nl>
10 * RĂ©mi Denis-Courmont <rem # videolan.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
38 # include <winsock2.h>
39 # include <ws2tcpip.h>
40 # define if_nametoindex( str ) atoi( str )
42 static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
43 # define close closesocket
45 #ifndef MCAST_JOIN_SOURCE_GROUP
47 * I hate manual definitions: Error-prone. Portability hell.
48 * Developers shall use UP-TO-DATE compilers. Full point.
49 * If you remove the warning, you remove the whole ifndef.
51 # warning Your C headers are out-of-date. Please update.
53 /* Most (all?) Mingw32 versions in use are yet to pick up Vista stuff */
54 # define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
55 struct group_source_req
57 uint32_t gsr_interface; /* interface index */
58 struct sockaddr_storage gsr_group; /* group address */
59 struct sockaddr_storage gsr_source; /* source address */
64 # include <sys/types.h>
66 # include <netdb.h> /* hostent ... */
67 # include <sys/socket.h>
68 # include <netinet/in.h>
74 /*****************************************************************************
76 *****************************************************************************/
77 static int OpenUDP( vlc_object_t * );
79 /*****************************************************************************
81 *****************************************************************************/
83 set_description( _("UDP/IPv6 network abstraction layer") );
84 set_capability( "network", 40 );
85 set_callbacks( OpenUDP, NULL );
88 /*****************************************************************************
89 * BuildAddr: utility function to build a struct sockaddr_in6
90 *****************************************************************************/
91 static int BuildAddr( vlc_object_t *p_this, struct sockaddr_in6 *p_socket,
92 const char *psz_address, int i_port )
94 struct addrinfo hints, *res;
97 memset( &hints, 0, sizeof( hints ) );
98 hints.ai_family = AF_INET6;
99 hints.ai_socktype = SOCK_DGRAM;
100 hints.ai_flags = AI_PASSIVE;
102 i = vlc_getaddrinfo( p_this, psz_address, 0, &hints, &res );
105 msg_Dbg( p_this, "%s: %s", psz_address, vlc_gai_strerror( i ) );
108 if ( res->ai_addrlen > sizeof (struct sockaddr_in6) )
110 vlc_freeaddrinfo( res );
114 memcpy( p_socket, res->ai_addr, res->ai_addrlen );
115 vlc_freeaddrinfo( res );
116 p_socket->sin6_port = htons( i_port );
121 #if defined(WIN32) || defined(UNDER_CE)
122 # define WINSOCK_STRERROR_SIZE 20
123 static const char *winsock_strerror( char *buf )
125 snprintf( buf, WINSOCK_STRERROR_SIZE, "Winsock error %d",
126 WSAGetLastError( ) );
127 buf[WINSOCK_STRERROR_SIZE - 1] = '\0';
133 /*****************************************************************************
134 * OpenUDP: open a UDP socket
135 *****************************************************************************
136 * psz_bind_addr, i_bind_port : address and port used for the bind()
137 * system call. If psz_bind_addr == NULL, the socket is bound to
138 * in6addr_any and broadcast reception is enabled. If psz_bind_addr is a
139 * multicast (FF00::/8) address, join the multicast group.
140 * psz_server_addr, i_server_port : address and port used for the connect()
141 * system call. It can avoid receiving packets from unauthorized IPs.
142 * Its use leads to great confusion and is currently discouraged.
143 * This function returns -1 in case of error.
144 *****************************************************************************/
145 static int OpenUDP( vlc_object_t * p_this )
147 network_socket_t *p_socket = p_this->p_private;
148 const char *psz_bind_addr = p_socket->psz_bind_addr;
149 int i_bind_port = p_socket->i_bind_port;
150 const char *psz_server_addr = p_socket->psz_server_addr;
151 int i_server_port = p_socket->i_server_port;
153 struct sockaddr_in6 sock;
155 #if defined(WIN32) || defined(UNDER_CE)
156 char strerror_buf[WINSOCK_STRERROR_SIZE];
157 # define strerror( x ) winsock_strerror( strerror_buf )
160 p_socket->i_handle = -1;
162 /* Build the local socket */
163 if ( BuildAddr( p_this, &sock, psz_bind_addr, i_bind_port ) == -1 )
166 /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
168 if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
170 msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
175 val.i_int = p_socket->v6only;
177 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val.i_int,
178 sizeof( val.i_int ) ) )
180 msg_Warn( p_this, "IPV6_V6ONLY: %s", strerror( errno ) );
181 p_socket->v6only = 1;
184 p_socket->v6only = 1;
188 # ifndef IPV6_PROTECTION_LEVEL
189 # define IPV6_PROTECTION_LEVEL 23
192 int i_val = 10 /*PROTECTION_LEVEL_UNRESTRICTED*/;
193 setsockopt( i_handle, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
198 /* We may want to reuse an already used socket */
200 if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
201 (void *) &i_opt, sizeof( i_opt ) ) == -1 )
203 msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
209 /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
210 * packet loss caused by scheduling problems */
212 if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
213 (void *) &i_opt, sizeof( i_opt ) ) == -1 )
215 msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
220 /* Under Win32 and for multicasting, we bind to IN6ADDR_ANY */
221 if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
223 struct sockaddr_in6 sockany = sock;
224 sockany.sin6_addr = in6addr_any;
225 sockany.sin6_scope_id = 0;
228 if( bind( i_handle, (struct sockaddr *)&sockany, sizeof( sock ) ) < 0 )
230 msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
238 if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
240 msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
245 /* Join the multicast group if the socket is a multicast address */
246 if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
250 struct group_source_req imr;
251 struct sockaddr_in6 *p_sin6;
253 imr.gsr_interface = 0;
254 imr.gsr_group.ss_family = AF_INET6;
255 imr.gsr_source.ss_family = AF_INET6;
256 p_sin6 = (struct sockaddr_in6 *)&imr.gsr_group;
257 p_sin6->sin6_addr = sock.sin6_addr;
259 /* Build socket for remote connection */
260 msg_Dbg( p_this, "psz_server_addr : %s", psz_server_addr);
262 if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) )
264 msg_Warn( p_this, "cannot build remote address" );
268 p_sin6 = (struct sockaddr_in6 *)&imr.gsr_source;
269 p_sin6->sin6_addr = sock.sin6_addr;
271 msg_Dbg( p_this, "MCAST_JOIN_SOURCE_GROUP multicast request" );
272 if( setsockopt( i_handle, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP,
273 (char *)&imr, sizeof(struct group_source_req) ) == -1 )
276 msg_Err( p_this, "Source specific multicast failed (%s) -"
277 " check if your OS really supports MLDv2",
283 struct ipv6_mreq imr;
286 imr.ipv6mr_interface = sock.sin6_scope_id;
287 imr.ipv6mr_multiaddr = sock.sin6_addr;
288 msg_Dbg( p_this, "IPV6_JOIN_GROUP multicast request" );
289 res = setsockopt(i_handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void*) &imr,
291 sizeof(imr) + 4); /* Doesn't work without this */
298 msg_Err( p_this, "cannot join multicast group" );
303 if( *psz_server_addr )
307 /* Build socket for remote connection */
308 if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
310 msg_Warn( p_this, "cannot build remote address" );
315 /* Connect the socket */
316 if( connect( i_handle, (struct sockaddr *) &sock,
317 sizeof( sock ) ) == (-1) )
319 msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
324 /* Set the time-to-live */
325 ttl = p_socket->i_ttl;
327 ttl = config_GetInt( p_this, "ttl" );
331 if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
333 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
334 (void *)&ttl, sizeof( ttl ) ) < 0 )
336 msg_Err( p_this, "failed to set multicast ttl (%s)",
342 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
343 (void *)&ttl, sizeof( ttl ) ) < 0 )
345 msg_Err( p_this, "failed to set unicast ttl (%s)",
351 /* Set multicast output interface */
352 if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
354 char *psz_mif = config_GetPsz( p_this, "miface" );
355 if( psz_mif != NULL )
357 int intf = if_nametoindex( psz_mif );
362 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
363 &intf, sizeof( intf ) ) < 0 )
365 msg_Err( p_this, "%s as multicast interface: %s",
366 psz_mif, strerror(errno) );
373 msg_Err( p_this, "%s: bad IPv6 interface specification",
382 p_socket->i_handle = i_handle;
384 var_Create( p_this, "mtu", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
385 var_Get( p_this, "mtu", &val );
386 p_socket->i_mtu = val.i_int;