ipv6.c 12.7 KB
Newer Older
1
2
3
/*****************************************************************************
 * ipv6.c: IPv6 network abstraction layer
 *****************************************************************************
4
 * Copyright (C) 2002-2006 the VideoLAN team
hartman's avatar
hartman committed
5
 * $Id$
6
7
8
9
 *
 * Authors: Alexis Guillard <alexis.guillard@bt.com>
 *          Christophe Massiot <massiot@via.ecp.fr>
 *          Remco Poortinga <poortinga@telin.nl>
10
 *          Rémi Denis-Courmont <rem # videolan.org>
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
26
27
28
29
30
31
32
33
34
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
#include <string.h>

#include <vlc/vlc.h>

35
36
#include <errno.h>

37
38
39
#ifdef WIN32
#   include <winsock2.h>
#   include <ws2tcpip.h>
40
41
42
43
#   define if_nametoindex( str ) atoi( str )
#else
#   include <sys/types.h>
#   include <unistd.h>
44
45
46
#   include <netdb.h>                                         /* hostent ... */
#   include <sys/socket.h>
#   include <netinet/in.h>
47
#   include <net/if.h>
48
49
50
51
52
53
#endif

#include "network.h"

#if defined(WIN32)
static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
54
# define close closesocket
55
56
#endif

57
#ifndef MCAST_JOIN_SOURCE_GROUP
58
59
60
61
62
63
# ifdef WIN32
/* Most (all?) Mingw32 versions in use are yet to pick up Vista stuff */
#  define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
# else
#  define MCAST_JOIN_SOURCE_GROUP 46
# endif
64
65
66
67
68
69
70
71
struct group_source_req
{
       uint32_t           gsr_interface;  /* interface index */
       struct sockaddr_storage gsr_group;      /* group address */
       struct sockaddr_storage gsr_source;     /* source address */
};
#endif

72
73
74
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
75
static int OpenUDP( vlc_object_t * );
76
77
78
79
80

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
81
    set_description( _("UDP/IPv6 network abstraction layer") );
82
    set_capability( "network", 40 );
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
83
    set_callbacks( OpenUDP, NULL );
84
85
86
87
88
vlc_module_end();

/*****************************************************************************
 * BuildAddr: utility function to build a struct sockaddr_in6
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
89
90
static int BuildAddr( vlc_object_t *p_this, struct sockaddr_in6 *p_socket,
                      const char *psz_address, int i_port )
91
92
{
    struct addrinfo hints, *res;
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
93
    int i;
94

Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
95
    memset( &hints, 0, sizeof( hints ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
96
    hints.ai_family = AF_INET6;
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
97
98
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE;
99

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
100
    i = vlc_getaddrinfo( p_this, psz_address, 0, &hints, &res );
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
101
    if( i )
102
    {
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
103
104
        msg_Dbg( p_this, "%s: %s", psz_address, vlc_gai_strerror( i ) );
        return -1;
105
    }
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
106
    if ( res->ai_addrlen > sizeof (struct sockaddr_in6) )
107
    {
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
108
109
        vlc_freeaddrinfo( res );
        return -1;
110
111
    }

Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
112
113
114
    memcpy( p_socket, res->ai_addr, res->ai_addrlen );
    vlc_freeaddrinfo( res );
    p_socket->sin6_port = htons( i_port );
115

116
    return 0;
117
118
}

119
120
121
122
123
124
125
126
127
128
129
130
#if defined(WIN32) || defined(UNDER_CE)
# define WINSOCK_STRERROR_SIZE 20
static const char *winsock_strerror( char *buf )
{
    snprintf( buf, WINSOCK_STRERROR_SIZE, "Winsock error %d",
              WSAGetLastError( ) );
    buf[WINSOCK_STRERROR_SIZE - 1] = '\0';
    return buf;
}
#endif


131
132
133
134
135
/*****************************************************************************
 * OpenUDP: open a UDP socket
 *****************************************************************************
 * psz_bind_addr, i_bind_port : address and port used for the bind()
 *   system call. If psz_bind_addr == NULL, the socket is bound to
136
137
 *   in6addr_any and broadcast reception is enabled. If psz_bind_addr is a
 *   multicast (FF00::/8) address, join the multicast group.
138
139
140
141
142
 * psz_server_addr, i_server_port : address and port used for the connect()
 *   system call. It can avoid receiving packets from unauthorized IPs.
 *   Its use leads to great confusion and is currently discouraged.
 * This function returns -1 in case of error.
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
143
static int OpenUDP( vlc_object_t * p_this )
144
{
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
145
146
    network_socket_t *p_socket = p_this->p_private;
    const char *psz_bind_addr = p_socket->psz_bind_addr;
147
    int i_bind_port = p_socket->i_bind_port;
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
148
    const char *psz_server_addr = p_socket->psz_server_addr;
149
    int i_server_port = p_socket->i_server_port;
150
    int i_handle, i_opt;
151
    struct sockaddr_in6 sock;
hartman's avatar
hartman committed
152
    vlc_value_t val;
153
154
155
156
#if defined(WIN32) || defined(UNDER_CE)
    char strerror_buf[WINSOCK_STRERROR_SIZE];
# define strerror( x ) winsock_strerror( strerror_buf )
#endif
157

158
159
    p_socket->i_handle = -1;

160
161
162
163
    /* Build the local socket */
    if ( BuildAddr( p_this, &sock, psz_bind_addr, i_bind_port ) == -1 )        
        return 0;

164
165
166
167
    /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
     * protocol */
    if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
    {
168
        msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
169
170
171
172
173
174
175
176
177
178
179
        return 0;
    }

#ifdef IPV6_V6ONLY
    val.i_int = p_socket->v6only;

    if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val.i_int,
                    sizeof( val.i_int ) ) )
    {
        msg_Warn( p_this, "IPV6_V6ONLY: %s", strerror( errno ) );
        p_socket->v6only = 1;
180
    }
181
182
183
#else
    p_socket->v6only = 1;
#endif
184

185
#ifdef WIN32
186
187
188
# ifndef IPV6_PROTECTION_LEVEL
#   define IPV6_PROTECTION_LEVEL 23
#  endif
189
    {
Marian Durkovic's avatar
Marian Durkovic committed
190
        int i_val = 10 /*PROTECTION_LEVEL_UNRESTRICTED*/;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
191
        setsockopt( i_handle, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
192
193
                    sizeof( i_val ) );
    }
194
195
#endif

196
197
198
199
200
    /* We may want to reuse an already used socket */
    i_opt = 1;
    if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
                    (void *) &i_opt, sizeof( i_opt ) ) == -1 )
    {
201
        msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
202
203
                         strerror(errno) );
        close( i_handle );
204
        return 0;
205
206
207
208
209
210
211
212
213
214
215
    }

    /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
     * packet loss caused by scheduling problems */
    i_opt = 0x80000;
    if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
                    (void *) &i_opt, sizeof( i_opt ) ) == -1 )
    {
        msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
                          strerror(errno) );
    }
gbazin's avatar
   
gbazin committed
216

gbazin's avatar
   
gbazin committed
217
218
219
220
221
222
223
224
225
226
227
#if defined(WIN32)
    /* Under Win32 and for multicasting, we bind to IN6ADDR_ANY */
    if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
    {
        struct sockaddr_in6 sockany = sock;
        sockany.sin6_addr = in6addr_any;
        sockany.sin6_scope_id = 0;

        /* Bind it */
        if( bind( i_handle, (struct sockaddr *)&sockany, sizeof( sock ) ) < 0 )
        {
228
            msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
gbazin's avatar
   
gbazin committed
229
            close( i_handle );
230
            return 0;
gbazin's avatar
   
gbazin committed
231
        }
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
232
233
    }
    else
gbazin's avatar
   
gbazin committed
234
#endif
235
236
237
    /* Bind it */
    if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
    {
238
        msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
239
        close( i_handle );
240
        return 0;
241
242
243
244
245
    }

    /* Join the multicast group if the socket is a multicast address */
    if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
    {
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
        if(*psz_server_addr)
        {
            struct group_source_req imr;
            struct sockaddr_in6 *p_sin6;

            imr.gsr_interface = 0;
            imr.gsr_group.ss_family = AF_INET6;
            imr.gsr_source.ss_family = AF_INET6;
            p_sin6 = (struct sockaddr_in6 *)&imr.gsr_group;
            p_sin6->sin6_addr = sock.sin6_addr;

            /* Build socket for remote connection */
            msg_Dbg( p_this, "psz_server_addr : %s", psz_server_addr);

            if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) )
            {
                msg_Warn( p_this, "cannot build remote address" );
                close( i_handle );
264
                return 0;
265
266
267
            }
            p_sin6 = (struct sockaddr_in6 *)&imr.gsr_source;
            p_sin6->sin6_addr = sock.sin6_addr;
268

269
            msg_Dbg( p_this, "MCAST_JOIN_SOURCE_GROUP multicast request" );
270
271
272
273
            if( setsockopt( i_handle, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP,
                          (char *)&imr, sizeof(struct group_source_req) ) == -1 )
            {

274
275
276
                msg_Err( p_this, "Source specific multicast failed (%s) -"
                          " check if your OS really supports MLDv2",
                          strerror(errno) );
277
            }
278
279
280
281
282
283
284
285
        }
        else
        {
            struct ipv6_mreq     imr;
            int                  res;

            imr.ipv6mr_interface = sock.sin6_scope_id;
            imr.ipv6mr_multiaddr = sock.sin6_addr;
286
            msg_Dbg( p_this, "IPV6_JOIN_GROUP multicast request" );
287
            res = setsockopt(i_handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void*) &imr,
gbazin's avatar
   
gbazin committed
288
289
290
#if defined(WIN32)
                         sizeof(imr) + 4); /* Doesn't work without this */
#else
291
                         sizeof(imr));
gbazin's avatar
   
gbazin committed
292
#endif
293

294
295
296
297
298
            if( res == -1 )
            {
                msg_Err( p_this, "cannot join multicast group" );
            } 
        }
299
    }
300
    else
301
302
    if( *psz_server_addr )
    {
303
        int ttl;
gbazin's avatar
   
gbazin committed
304

305
        /* Build socket for remote connection */
gbazin's avatar
   
gbazin committed
306
        if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
307
        {
308
            msg_Warn( p_this, "cannot build remote address" );
309
            close( i_handle );
310
            return 0;
311
        }
gbazin's avatar
   
gbazin committed
312

313
314
315
316
        /* Connect the socket */
        if( connect( i_handle, (struct sockaddr *) &sock,
                     sizeof( sock ) ) == (-1) )
        {
317
            msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
318
            close( i_handle );
319
            return 0;
320
        }
gbazin's avatar
   
gbazin committed
321
322

        /* Set the time-to-live */
323
324
325
326
        ttl = p_socket->i_ttl;
        if( ttl <= 0 )
            ttl = config_GetInt( p_this, "ttl" );

327
        if( ttl > 0 )
gbazin's avatar
   
gbazin committed
328
329
330
331
332
333
        {
            if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
            {
                if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
                                (void *)&ttl, sizeof( ttl ) ) < 0 )
                {
334
335
                    msg_Err( p_this, "failed to set multicast ttl (%s)",
                             strerror(errno) );
gbazin's avatar
   
gbazin committed
336
337
338
339
340
341
342
                }
            }
            else
            {
                if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
                                (void *)&ttl, sizeof( ttl ) ) < 0 )
                {
343
                    msg_Err( p_this, "failed to set unicast ttl (%s)",
gbazin's avatar
   
gbazin committed
344
345
346
347
                              strerror(errno) );
                }
            }
        }
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370

        /* Set multicast output interface */
        if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
        {
            char *psz_mif = config_GetPsz( p_this, "miface" );
            if( psz_mif != NULL )
            {
                int intf = if_nametoindex( psz_mif );
                free( psz_mif  );

                if( intf != 0 )
                {
                    if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
                        &intf, sizeof( intf ) ) < 0 )
                    {
                        msg_Err( p_this, "%s as multicast interface: %s",
                                 psz_mif, strerror(errno) );
                        close( i_handle );
                        return 0;
                    }
                }
                else
                {
371
                    msg_Err( p_this, "%s: bad IPv6 interface specification",
372
373
374
375
376
377
                             psz_mif );
                    close( i_handle );
                    return 0;
                }
            }
        }
378
379
380
    }

    p_socket->i_handle = i_handle;
hartman's avatar
hartman committed
381
382
383
384

    var_Create( p_this, "mtu", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "mtu", &val );
    p_socket->i_mtu = val.i_int;
385

386
    return 0;
387
}