[vlc-devel] [PATCH] AMT = Automatic Multicast Tunneling
Rémi Denis-Courmont
remi at remlab.net
Fri Jul 27 17:46:55 CEST 2018
Le perjantaina 27. heinäkuuta 2018, 18.29.39 EEST Natalie Landsberg a écrit :
> This module aims to provide access to multicast streams from a
> non-multicast enabled network. So, if you have a stream coming
> from I2, you should be able to access it from outside of I2 using
> this module.
>
> Essentially it creates a tunnel for you to have access to the stream.
>
> It works similarly to udp native multicast streaming:
> -provide server address
> -provide multicast group address
> -(optional) provide AMT relay address
>
> There exists a relay which can be used to test the stream. Can be
> provided upon request.
> ---
> modules/MODULES_LIST | 1 +
> modules/access/Makefile.am | 8 +
> modules/access/amt.c | 967 +++++++++++++++++++++++++++++++++++++
> modules/access/amt.h | 217 +++++++++
> 4 files changed, 1193 insertions(+)
> create mode 100644 modules/access/amt.c
> create mode 100644 modules/access/amt.h
>
> diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
> index e1978cc6e4..0e6a015384 100644
> --- a/modules/MODULES_LIST
> +++ b/modules/MODULES_LIST
> @@ -33,6 +33,7 @@ $Id$
> * alphamask: Alpha layer mask video filter
> * alsa: audio output module using the ALSA API
> * amem: audio memory output
> + * amt: automatic multicast tunneling for multicast streaming
> * anaglyph: anaglyph 3d video filter
> * android_audiotrack: audio output for Android, based on AudioTrack
> * android_display: Android native window provider module
> diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
> index 70050ad21e..903fb43d1f 100644
> --- a/modules/access/Makefile.am
> +++ b/modules/access/Makefile.am
> @@ -369,6 +369,14 @@ libudp_plugin_la_SOURCES = access/udp.c
> libudp_plugin_la_LIBADD = $(SOCKET_LIBS)
> access_LTLIBRARIES += libudp_plugin.la
>
> +libamt_plugin_la_SOURCES = \
> + access/amt.c \
> + acces/amt.h
> +libamt_plugin_la_LIBADD = $(SOCKET_LIBS) $(LIBPTHREAD)
There is no LIBPTHREAD (anymore).
> +libamt_plugin_la_LDFLAGS = $(AM_LD_FLAGS) -rpath '$(accessdir)'
> +libamt_plugin_la_CFLAGS = $(AM_CFLAGS)
> +access_LTLIBRARIES += libamt_plugin.la
> +
> libunc_plugin_la_SOURCES = access/unc.c access/smb_common.h
> libunc_plugin_la_LIBADD = -lmpr -lnetapi32
> if HAVE_WIN32
> diff --git a/modules/access/amt.c b/modules/access/amt.c
> new file mode 100644
> index 0000000000..ce53d85412
> --- /dev/null
> +++ b/modules/access/amt.c
> @@ -0,0 +1,967 @@
> +/**
> + * @file amt.c
> + * @brief Automatic Multicast Tunneling Protocol (AMT) helper file for VLC
> media player + * Allows multicast streaming when not in a multicast-enabled
> network + * Currently IPv4 is supported, but IPv6 is not yet.
> + * Copyright (c) Juniper Networks, Inc., 2018 - 2018. All rights reserved.
You cannot simply remove copyright statements from the source material, i.e.
udp.c.
> + *
> + * This code is licensed to you under the GNU Lesser General Public License
> + * version 2.1 or later. You may not use this code except in compliance
> with + * the GNU Lesser General Public License.
> + * This code is not an official Juniper product.
> + * Authors: Natalie Landsberg <nlandsberg at juniper.net>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * as published by the Free Software Foundation; either version 2.1
> + * of the License, or (at your option) any later version.
> + *
> + * This library 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 Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
> USA +
> ***************************************************************************
> */ +#ifdef HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +
> +#include <errno.h>
> +#include <ctype.h>
> +#ifdef HAVE_ARPA_INET_H
> +# include <arpa/inet.h>
> +#endif
> +#include <sys/select.h>
> +#include <sys/socket.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_demux.h>
> +#include <vlc_plugin.h>
> +#include <vlc_access.h>
> +#include <vlc_network.h>
> +#include <vlc_block.h>
> +#include <vlc_interrupt.h>
> +
> +#include "amt.h"
> +
> +#ifdef HAVE_POLL
> + #include <poll.h>
> +#endif
> +#ifdef HAVE_SYS_UIO_H
> + #include <sys/uio.h>
> +#endif
> +
> +/**************************************************************************
> *** + * Module descriptor
> +
> ***************************************************************************
> **/ +
> +#ifndef AMT_PORT
Useless.
> + #define AMT_PORT 2268 /* IANA */
> +#endif
> +
> +static int Open( vlc_object_t * );
> +static void Close( vlc_object_t * );
Reorder the code to avoid forward declarations.
> +
> +#define BUFFER_TEXT N_("Receive buffer")
> +#define BUFFER_LONGTEXT N_("AMT receive buffer size (bytes)" )
> +#define TIMEOUT_TEXT N_("AMT Source timeout (sec)")
> +#define AMT_TIMEOUT_TEXT N_("AMT timeout (sec)")
> +
> +vlc_module_begin ()
> + set_shortname( N_("AMT" ) )
> + set_description( N_("AMT input") )
> + set_category( CAT_INPUT )
> + set_subcategory( SUBCAT_INPUT_ACCESS )
> +
> + add_integer( "amt-timeout", 5, AMT_TIMEOUT_TEXT, NULL, true )
> +
> + set_capability( "access", 0 )
> + add_shortcut( "amt" )
> +
> + set_callbacks( Open, Close )
> +vlc_module_end ()
> +
> +/**************************************************************************
> *** + * Local prototypes
> +
> ***************************************************************************
> **/ +static block_t *BlockUDP( stream_t *, bool * );
> +static int Control( stream_t *, int, va_list );
Ditto.
> +
> +/**************************************************************************
> *** + * Open: open the socket
> +
> ***************************************************************************
> **/ +static int Open( vlc_object_t *p_this )
> +{
> + stream_t *p_access = (stream_t*)p_this;
> + access_sys_t *sys;
> +
> + if( p_access->b_preparsing )
> + return VLC_EGENERIC;
> +
> + sys = vlc_obj_malloc( p_this, sizeof( *sys ) );
> + if( unlikely( sys == NULL ) )
> + return VLC_ENOMEM;
> +
> + p_access->p_sys = sys;
> +
> + /* Set up p_access */
> + ACCESS_SET_CALLBACKS( NULL, BlockUDP, Control, NULL );
> +
> + char *psz_name = strdup( p_access->psz_location );
> + char *psz_parser;
> + const char *psz_server_addr, *psz_bind_addr, *psz_relay_addr = "";
> + int i_bind_port = 1234, i_server_port = 0;
> +
> + if( unlikely(psz_name == NULL) )
> + return VLC_ENOMEM;
> +
> + /* Parse psz_name syntax :
> + * [serveraddr[:serverport]][@[bindaddr]:[bindport]]:[amt=relayip] */
> +
> + psz_parser = strchr( psz_name, '@' );
> + if( psz_parser != NULL )
> + {
> + /* Found bind address and/or bind port */
> + *psz_parser++ = '\0';
> + psz_bind_addr = psz_parser;
> +
> + if( psz_bind_addr[0] == '[' )
> + /* skips bracket'd IPv6 address */
> + psz_parser = strchr( psz_parser, ']' );
> +
> + if( psz_parser != NULL )
> + {
> + psz_parser = strchr( psz_parser, ':' );
> + if( psz_parser != NULL )
> + {
> + *psz_parser++ = '\0';
> + if( psz_parser[0] == 'a' && psz_parser[1] == 'm' &&
> psz_parser[2] == 't') + {
> + psz_parser = strchr( psz_parser, '=' );
> + if( psz_parser != NULL )
> + {
> + *psz_parser++ = '\0';
> + psz_relay_addr = psz_parser;
> + }
> + } else
> + i_bind_port = atoi( psz_parser );
> + }
> + }
> + }
> +
> + psz_server_addr = psz_name;
> + psz_parser = ( psz_server_addr[0] == '[' )
> + ? strchr( psz_name, ']' ) /* skips bracket'd IPv6 address */
> + : psz_name;
> +
> + if( psz_parser != NULL )
> + {
> + psz_parser = strchr( psz_parser, ':' );
> + if( psz_parser != NULL )
> + {
> + *psz_parser++ = '\0';
> + i_server_port = atoi( psz_parser );
> + }
> + }
> +
> + msg_Dbg( p_access, "opening server=%s:%d local=%s:%d relay addr=%s",
> + psz_server_addr, i_server_port, psz_bind_addr, i_bind_port,
> psz_relay_addr ); +
> + if( strlen( psz_bind_addr ) > 0 )
> + {
> + sys->mcastGroup = malloc( strlen( psz_bind_addr ) );
Missing error check.
vlc_obj_malloc() would probably be simpler.
Ditto below.
> + memset( sys->mcastGroup, 0, strlen(psz_bind_addr) );
This is redundant.
Ditto below.
> + memcpy( sys->mcastGroup, psz_bind_addr, strlen( psz_bind_addr ) );
> +
> + bool done = false;
> + while( !done ) {
> + if( !isdigit( (int)( sys->mcastGroup[ strlen( sys->mcastGroup )
> - 1 ] ) ) ||
> + strlen( psz_bind_addr ) < strlen(
> sys->mcastGroup - 1) ) {
> + sys->mcastGroup[ strlen(
> sys->mcastGroup ) - 1] = '\0';
> + } else {
> + done = true;
break;
But this block looks rather suspicious.
> + }
> + }
> + }
> +
> + if( strlen( psz_server_addr) > 0 )
> + {
> + sys->srcAddr = malloc( strlen( psz_server_addr ) );
> + memset(sys->srcAddr, 0, strlen(psz_server_addr) );
> + memcpy( sys->srcAddr, psz_server_addr, strlen( psz_server_addr ) );
> + }
> +
> + if( strlen( psz_relay_addr ) > 0 )
> + {
> + sys->relayAddr = malloc( strlen( psz_relay_addr ) );
> + memset(sys->relayAddr, 0, strlen(psz_relay_addr) );
> + memcpy( sys->relayAddr, psz_relay_addr, strlen( psz_server_addr )
> ); + } else {
> + sys->relayAddr = malloc( sizeof( "198.38.23.145" ) );
> + memset(sys->relayAddr, 0, sizeof( "198.38.23.145" ) );
> + memcpy( sys->relayAddr, "198.38.23.145", sizeof( "198.38.23.145" )
> );
I would rather VLC not violate RFC4085.
> + }
> +
> + msg_Dbg( p_access, "Addresses: mcastGroup: %s srcAddr: %s relayAddr:
> %s", \ + sys->mcastGroup, sys->srcAddr, sys->relayAddr);
> +
> + sys->fd = net_OpenDgram( p_access, psz_bind_addr, i_bind_port,
> + psz_server_addr, i_server_port, IPPROTO_UDP );
> + free( psz_name );
> + if( sys->fd == -1 )
> + {
> + msg_Err( p_access, "cannot open socket" );
> + return VLC_EGENERIC;
> + }
> +
> + sys->mtu = 1500;
> +
> + sys->timeout = var_InheritInteger( p_access, "udp-timeout");
> + if( sys->timeout > 0)
> + sys->timeout *= 1000;
I think there are macros for this now.
> +
> + sys->amtTimeout = var_InheritInteger( p_access, "amt-timeout" );
> + if( sys->amtTimeout > 0)
> + sys->amtTimeout *= 1000;
> +
> + sys->triedAMT = false;
> +
> + return VLC_SUCCESS;
> +}
> +
> +/**************************************************************************
> *** + * Close: free unused data structures
> +
> ***************************************************************************
> **/ +static void Close( vlc_object_t *p_this )
> +{
> + stream_t *p_access = (stream_t*)p_this;
> + access_sys_t *sys = p_access->p_sys;
> +
> + amt_leaveSSM_group( sys, p_access );
> + amt_send_mem_update( sys, p_access, true );
> +
> + net_Close( sys->fd );
> + net_Close( sys->sAMT );
> + net_Close( sys->sQuery );
> +
> + free( sys->mcastGroup );
> + free( sys->srcAddr );
> + free( sys->relayAddr);
> +}
> +
> +/**************************************************************************
> *** + * Control:
> +
> ***************************************************************************
> **/ +static int Control( stream_t *p_access, int i_query, va_list args ) +{
> + bool *pb_bool;
> + int64_t *pi_64;
> +
> + switch( i_query )
> + {
> + case STREAM_CAN_SEEK:
> + case STREAM_CAN_FASTSEEK:
> + case STREAM_CAN_PAUSE:
> + case STREAM_CAN_CONTROL_PACE:
> + pb_bool = va_arg( args, bool * );
> + *pb_bool = false;
> + break;
> +
> + case STREAM_GET_PTS_DELAY:
> + pi_64 = va_arg( args, int64_t * );
> + *pi_64 = INT64_C(1000)
> + * var_InheritInteger(p_access, "network-caching");
Also here.
> + break;
> +
> + default:
> + return VLC_EGENERIC;
> + }
> + return VLC_SUCCESS;
> +}
> +
> +/**************************************************************************
> *** + * BlockUDP:
> +
> ***************************************************************************
> **/ +static block_t *BlockUDP(stream_t *access, bool *restrict eof)
> +{
> + access_sys_t *sys = access->p_sys;
> +
> + block_t *pkt = block_Alloc(sys->mtu);
> + if (unlikely(pkt == NULL))
> + { /* OOM - dequeue and discard one packet */
> + char dummy;
> + recv(sys->fd, &dummy, 1, 0);
> + return NULL;
> + }
> +
> + struct iovec iov = {
> + .iov_base = pkt->p_buffer,
> + .iov_len = sys->mtu,
> + };
> + struct msghdr msg = {
> + .msg_iov = &iov,
> + .msg_iovlen = 1,
> +#ifdef __linux__
> + .msg_flags = MSG_TRUNC,
> +#endif
> + };
> +
> + struct pollfd ufd[1];
> +
> + if( sys->triedAMT )
> + ufd[0].fd = sys->sAMT;
> + else
> + ufd[0].fd = sys->fd;
> + ufd[0].events = POLLIN;
> +
> + switch (vlc_poll_i11e(ufd, 1, sys->timeout))
> + {
> + case 0:
> + msg_Err(access, "receive time-out");
???
> + if( !sys->triedAMT )
> + {
This whole block does not look like it belongs in the Block function.
> + msg_Dbg( access, "Attempting AMT...");
> + sys->triedAMT = true;
> +
> + int init = amt_sockets_init( sys, access );
> + if( init != 0 )
> + {
> + msg_Err( access, "Error initializing sockets" );
> + goto skip;
> + }
> +
> + amt_send_relay_discovery_msg( sys, access );
> + msg_Dbg( access, "Sent AMT discovery message" );
> + if( amt_rcv_relay_adv( sys, access ) )
> + {
> + msg_Dbg( access, "Received relay adv" );
> + amt_send_relay_request( sys, access );
> + } else
> + {
> + msg_Err( access, "Error receiving relay adv msg,
> skipping" );
> + goto skip;
> + }
> +
> + if( amt_rcv_relay_mem_query( sys, access ) )
> + {
> + msg_Dbg( access, "Received mem query" );
> + if( amt_joinSSM_group( sys, access ) != 0 )
> + {
> + msg_Err(access, "Error joining SSM %s",
> strerror(errno)); + goto skip;
> + }
> + msg_Dbg( access, "Joined SSM group" );
> + amt_send_mem_update( sys, access, false );
> + }
> +
> + sys->queryTime = time( NULL );
vlc_tick_now()
> + }
> + else
> + {
> + msg_Err(access, "AMT receive time-out");
> + *eof = true;
> + }
> + /* fall through */
> + case -1:
> + goto skip;
> + }
> +
> + ssize_t len;
> + if( sys->triedAMT )
> + {
> + if( (time(NULL) - sys->queryTime) > sys->relay_igmp_query.qqic )
> + {
> + amt_send_mem_update( sys, access, false );
> + sys->queryTime = time(NULL);
> + }
> +
> + char amtpkt[sys->mtu];
No VLA with attacker controller size.
> + struct sockaddr tmp;
> + u32 tmp_size = sizeof( struct sockaddr );
> + len = recvfrom( sys->sAMT, &amtpkt, sizeof( amtpkt ), 0, &tmp,
> &tmp_size ); +
> + if( len < 0 )
> + goto skip;
> +
> + if( amtpkt[0] != AMT_MULT_DATA )
> + {
> + msg_Dbg( access, "Not AMT multicast data, dropping.");
> + goto skip;
> + }
> +
> + amt_multicast_data_t *temp = malloc( sizeof( amt_multicast_data_t )
> ); + memset(temp, 0, sizeof( amt_multicast_data_t ) );
> + temp->type = amtpkt[0];
> + temp->resv = amtpkt[1];
> + memcpy( (void *)&temp->ip, &amtpkt[AMT_HDR_LEN], IP_HDR_LEN );
> + memcpy( (void *)&temp->udp, &amtpkt[IP_HDR_LEN + AMT_HDR_LEN],
> UDP_HDR_LEN ); +
> + ssize_t shift = IP_HDR_LEN + UDP_HDR_LEN + AMT_HDR_LEN;
> + temp->buf = malloc( (len - shift) );
> + memcpy( temp->buf, &amtpkt[shift], (len - shift) );
> +
> + if( len < shift )
> + {
> + msg_Err(access, "%zd bytes packet truncated (MTU was %zu)",
> + len, sys->mtu);
> + pkt->i_flags |= BLOCK_FLAG_CORRUPTED;
> + sys->mtu = len;
> + } else {
> + len -= shift;
> + }
> +
> + memcpy( pkt->p_buffer, &amtpkt[shift], len );
> + free( temp->buf );
> + free( temp );
> +
> + }
> + else
> + len = recvmsg(sys->fd, &msg, 0);
> +
> + if (len < 0)
> + {
> +skip:
> + block_Release(pkt);
> + return NULL;
> + }
> +
> +#ifdef MSG_TRUNC
> + if (msg.msg_flags & MSG_TRUNC)
> + {
> + msg_Err(access, "%zd bytes packet truncated (MTU was %zu)",
> + len, sys->mtu);
> + pkt->i_flags |= BLOCK_FLAG_CORRUPTED;
> + sys->mtu = len;
> + }
> + else
> +#endif
> + pkt->i_buffer = len;
> +
> + return pkt;
> +}
> +
> +/**
> + * Calculate checksum
> + * */
> +u16 getChecksum( u16 *buffer, int nLen )
Use standard ISO C types for fixes size integers.
> +{
> + int nleft = nLen;
> + int sum = 0;
> + unsigned short *w = buffer;
> + unsigned short answer = 0;
> +
> + while (nleft > 1)
> + {
> + sum += *w++;
> + nleft -= 2;
> + }
> + if (nleft == 1)
> + {
> + *(unsigned char*)(&answer) = *(unsigned char*)w;
> + sum += answer;
> + }
> + sum = (sum >> 16) + (sum & 0xffff);
> + answer = ~sum;
> + return (answer);
> +}
> +
> +/**
> + * Make IGMP Membershipt report
> + * */
> +void makeReport( amt_igmpv3_membership_report_t *mr )
> +{
> + mr->type = AMT_IGMPV3_MEMBERSHIP_REPORT_TYPEID;
> + mr->resv = 0;
> + mr->checksum = 0;
> + mr->resv2 = 0;
> + mr->nGroupRecord = htons(1);
> +}
> +
> +/**
> + * Make IP header
> + * */
> +void makeIPHeader( amt_ip_alert_t *p_ipHead )
> +{
> + p_ipHead->ver_ihl = 0x46;
> + p_ipHead->tos = 0xc0;
> + p_ipHead->tot_len = htons( IP_HDR_IGMP_LEN
> +
> sizeof(amt_igmpv3_membership_report_t) );
There are no guarantees that the type represenation has the correct size, as
the compiler can add padding.
> + p_ipHead->id = 0x00;
> + p_ipHead->frag_off = 0x0000;
> + p_ipHead->ttl = 0x01;
> + p_ipHead->protocol = 0x02;
> + p_ipHead->check = 0;
> + p_ipHead->srcAddr = INADDR_ANY;
> + p_ipHead->options = 0x0000;
> +}
> +
> +/** Create relay discovery socket, query socket, UDP socket and
> + * fills in relay anycast address for discovery
> + * return 0 if successful, -1 if not
> + */
> +int amt_sockets_init( access_sys_t *sys, stream_t *p_access )
> +{
> + struct sockaddr_in rcvAddr;
> + memset( &rcvAddr, 0, sizeof(struct sockaddr_in) );
> + int enable = 1;
> +
> + /* Relay anycast address for discovery */
> + sys->relayAddrDisco.sin_family = AF_INET;
> + sys->relayAddrDisco.sin_port = htons( AMT_PORT );
> + int res = inet_pton( AF_INET, sys->relayAddr,
> &sys->relayAddrDisco.sin_addr ); + if( res != 1 )
> + msg_Err( p_access, "Could not convert relay anycast" );
> +
> + /* create UDP socket */
> + sys->sAMT = vlc_socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP, true );
> + if( sys->sAMT == -1 )
> + {
> + msg_Err( p_access, "Failed to create UDP socket" );
> + return -1;
> + }
> +
> + res = setsockopt(sys->sAMT, IPPROTO_IP, IP_PKTINFO, &enable,
> sizeof(enable)); + if(res < 0)
That option is only standard for IPv6, so there may be some compilation issues
here.
> + {
> + msg_Err( p_access, "Couldn't set socket options for IPPROTO_IP,
> IP_PKTINFO\n %s", strerror(errno)); + return -1;
> + }
> +
> + res = setsockopt(sys->sAMT, SOL_SOCKET, SO_REUSEADDR, &enable,
> sizeof(enable)); + if(res < 0)
> + {
> + msg_Err( p_access, "Couldn't make socket reusable");
Obvious leak. Ditto below.
> + return -1;
> + }
> +
> + rcvAddr.sin_family = AF_INET;
> + rcvAddr.sin_port = htons( 0 );
> + rcvAddr.sin_addr.s_addr = INADDR_ANY;
> +
> + if( bind(sys->sAMT, (struct sockaddr *)&rcvAddr, sizeof(rcvAddr) ) != 0
> ) + {
> + msg_Err( p_access, "Failed to bind UDP socket error: %s",
> strerror(errno) ); + return -1;
> + }
> +
> + sys->sQuery = vlc_socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP, true );
> + if( sys->sQuery == -1 )
> + {
> + msg_Err( p_access, "Failed to create query socket" );
> + return -1;
> + }
> +
> + /* bind socket to local address */
> + sys->stLocalAddr.sin_family = AF_INET;
> + sys->stLocalAddr.sin_port = htons( 0 );
> + sys->stLocalAddr.sin_addr.s_addr = INADDR_ANY;
> +
> + if( bind(sys->sQuery, (struct sockaddr *)&sys->stLocalAddr,
> sizeof(struct sockaddr) ) != 0 ) + {
> + msg_Err( p_access, "Failed to bind query socket" );
> + return -1;
> + }
> +
> + sys->stSvrAddr.sin_family = AF_INET;
> + sys->stSvrAddr.sin_port = htons( 9124 );
> + res = inet_aton( "127.0.0.1", &sys->stSvrAddr.sin_addr );
> + if( res == 0 )
> + {
> + msg_Err( p_access, "Could not convert loopback address" );
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * Send a relay discovery message, before 3-way handshake
> + * */
> +void amt_send_relay_discovery_msg( access_sys_t *sys, stream_t *p_access )
> +{
> + char chaSendBuffer[AMT_DISCO_MSG_LEN];
> + unsigned int ulNonce;
> + int nRet;
> +
> + /* initialize variables */
> + memset( chaSendBuffer, 0, sizeof(chaSendBuffer) );
> + ulNonce = 0;
> + nRet = 0;
> +
> + /*
> + * create AMT discovery message format
> + * +---------------------------------------------------+
> + * | Msg Type(1Byte)| Reserved (3 byte)| nonce (4byte) |
> + * +---------------------------------------------------+
> + */
> +
> + chaSendBuffer[0] = AMT_RELAY_DISCO;
> + chaSendBuffer[1] = 0;
> + chaSendBuffer[2] = 0;
> + chaSendBuffer[3] = 0;
> +
> + /* create nonce and copy into send buffer */
> + srand( (unsigned int)time(NULL) );
> + ulNonce = htonl( rand() );
> + memcpy( (void*)&chaSendBuffer[4], (void*)&ulNonce, sizeof(ulNonce) );
> + sys->glob_ulNonce = ulNonce;
> +
> + /* send it */
> + nRet = sendto( sys->sAMT, chaSendBuffer, sizeof(chaSendBuffer), 0,\
> + (struct sockaddr *)&sys->relayAddrDisco, sizeof(struct
> sockaddr) ); +
> + if( nRet < 0)
> + {
> + msg_Err( p_access, "Sendto failed with error %d.", errno);
> + return;
> + }
> +
> +}
> +
> +/**
> + * Send relay request message, stage 2 of handshake
> + * */
> +void amt_send_relay_request( access_sys_t *sys, stream_t *p_access )
> +{
> + char chaSendBuffer[AMT_REQUEST_MSG_LEN];
> + unsigned int ulNonce;
> + int nRet;
> + int nRetry;
> +
> + memset( chaSendBuffer, 0, sizeof(chaSendBuffer) );
> +
> + ulNonce = 0;
> + nRet = 0;
> + nRetry = 0;
> +
> + /*
> + * create AMT request message format
> + * +-----------------------------------------------------------------+
> + * | Msg Type(1Byte)| Reserved(1byte)|P flag(1byte)|Reserved (2 byte)|
> + * +-----------------------------------------------------------------+
> + * | nonce (4byte) |
> + * +-----------------------------------------------------------------+
> + *
> + * The P flag is set to indicate which group membership protocol the
> + * gateway wishes the relay to use in the Membership Query response:
> +
> + * Value Meaning
> +
> + * 0 The relay MUST respond with a Membership Query message that
> + * contains an IPv4 packet carrying an IGMPv3 General Query
> + * message.
> + * 1 The relay MUST respond with a Membership Query message that
> + * contains an IPv6 packet carrying an MLDv2 General Query
> + * message.
> + *
> + */
> +
> + chaSendBuffer[0] = AMT_REQUEST;
> + chaSendBuffer[1] = 0;
> + chaSendBuffer[2] = 0;
> + chaSendBuffer[3] = 0;
> +
> + ulNonce = sys->glob_ulNonce;
> + memcpy( (void*)&chaSendBuffer[4], (void*)&ulNonce, sizeof(unsigned int) );
> +
> + nRet = send( sys->sAMT, chaSendBuffer, sizeof(chaSendBuffer), 0 );
> +
> + if( nRet < 0 )
> + {
> + msg_Err(p_access, "Error sending relay request error: %s",
> strerror(errno) ); + return;
> + }
> +}
> +
> +/*
> +* create AMT request message format
> +*
> +--------------------------------------------------------------------------
> --------+ +* | Msg Type(1 byte)| Reserved (1 byte)| MAC (6 byte)| nonce (4
> byte) | IGMP packet | +*
> +--------------------------------------------------------------------------
> --------+ +*/
> + void amt_send_mem_update( access_sys_t *sys, stream_t *p_access, bool
> leave) +{
> + int sendBufSize = IP_HDR_IGMP_LEN + MAC_LEN + NONCE_LEN + AMT_HDR_LEN;
> + char pSendBuffer[ sendBufSize +
> (int)sizeof(amt_igmpv3_membership_report_t)]; + unsigned int ulNonce =
> 0;
> + int nRet = 0;
> + memset( &pSendBuffer, 0, sizeof(pSendBuffer) );
> +
> + pSendBuffer[0] = AMT_MEM_UPD;
> +
> + /* copy relay MAC response */
> + memcpy( (void*)&pSendBuffer[2],
> (void*)sys->relay_mem_query_msg.uchaMAC, MAC_LEN ); +
> + /* copy nonce */
> + ulNonce = ntohl(sys->glob_ulNonce);
> + memcpy( (void*)&pSendBuffer[8], (void*)&ulNonce, NONCE_LEN );
> +
> + /* make IP header for IGMP packet */
> + amt_ip_alert_t p_ipHead;
> + memset( &p_ipHead, 0, IP_HDR_IGMP_LEN );
> + makeIPHeader( &p_ipHead );
> +
> + struct sockaddr_in temp;
> + if( inet_pton( AF_INET, MCAST_ALLHOSTS, &(temp.sin_addr) ) != 1 )
> + {
> + msg_Err( p_access, "Error converting %s", MCAST_ALLHOSTS );
> + }
> + p_ipHead.destAddr = temp.sin_addr.s_addr;
> + p_ipHead.check = getChecksum( (u16*)&p_ipHead,
> (int)sizeof(amt_ip_alert_t) ); +
> + amt_igmpv3_groupRecord_t groupRcd;
> + groupRcd.type = leave ? AMT_IGMP_BLOCK:AMT_IGMP_ALLOW;
> + groupRcd.auxDatalen = 0;
> + groupRcd.nSrc = htons(1);
> +
> + if( inet_pton( AF_INET, sys->mcastGroup, &(temp.sin_addr) ) != 1 )
> + {
> + msg_Err( p_access, "Error converting multicast group address" );
> + }
> + groupRcd.ssm = temp.sin_addr.s_addr;
> +
> + if( inet_pton( AF_INET, sys->srcAddr, &(temp.sin_addr) ) != 1 )
> + {
> + msg_Err( p_access, "Error converting src address" );
> + }
> + groupRcd.srcIP[0] = temp.sin_addr.s_addr;
> +
> + /* make IGMP membership report */
> + amt_igmpv3_membership_report_t p_igmpMemRep;
> + makeReport( &p_igmpMemRep );
> +
> + memcpy((void *)&p_igmpMemRep.grp[0], (void *)&groupRcd,
> (int)sizeof(groupRcd) ); + p_igmpMemRep.checksum = getChecksum(
> (u16*)&p_igmpMemRep, (int)sizeof(amt_igmpv3_membership_report_t) ); +
> + amt_membership_update_msg_t memUpdateMsg;
> + memset((void*)&memUpdateMsg, 0, sizeof(memUpdateMsg));
> + memcpy((void*)&memUpdateMsg.ipHead, (void*)&p_ipHead, sizeof(p_ipHead)
> ); + memcpy((void*)&memUpdateMsg.memReport, (void*)&p_igmpMemRep,
> sizeof(p_igmpMemRep) ); +
> + memcpy( (void*)&pSendBuffer[12], (void*)&memUpdateMsg,
> sizeof(memUpdateMsg) ); +
> + nRet = send( sys->sAMT, pSendBuffer, sizeof(pSendBuffer), 0 );
> +
> + if( nRet < 0 )
> + {
> + msg_Err(p_access, "Error sending membership update error: %s",
> strerror(errno) ); + return;
> + }
> +}
> +
> +/**
> + * Receive relay advertisement message
> + *
> + *
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + * | V=0 |Type=2 | Reserved |
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + * | Discovery Nonce |
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + * | |
> + * ~ Relay Address (IPv4 or IPv6) ~
> + * | |
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + * */
> +bool amt_rcv_relay_adv( access_sys_t *sys, stream_t *p_access )
> +{
> + char pkt[RELAY_ADV_MSG_LEN];
> + memset( &pkt, 0, RELAY_ADV_MSG_LEN );
> +
> + struct pollfd ufd[1];
> +
> + ufd[0].fd = sys->sAMT;
> + ufd[0].events = POLLIN;
> +
> + switch( vlc_poll_i11e(ufd, 1, sys->timeout) )
> + {
> + case 0:
> + msg_Err(p_access, "relay adv receive time-out");
> + /* fall through */
> + case -1:
> + goto skip;
> + }
> +
> + struct sockaddr temp;
> + u32 temp_size = sizeof( struct sockaddr );
> + ssize_t len = recvfrom( sys->sAMT, &pkt, RELAY_ADV_MSG_LEN, 0, &temp,
> &temp_size ); +
> + if (len < 0)
> + {
> + skip:
> + msg_Err(p_access, "length less than zero");
> + return false;
> + }
> +
> + memcpy( (void*)&sys->relay_adv_msg.type, &pkt[0], MSG_TYPE_LEN );
> + if( sys->relay_adv_msg.type != AMT_RELAY_ADV )
> + {
> + msg_Err( p_access, "Received message not relay advertisement,
> ignoring. "); + return false;
> + }
> +
> + memcpy( (void*)&sys->relay_adv_msg.ulRcvNonce, &pkt[NONCE_LEN],
> NONCE_LEN ); + if( sys->glob_ulNonce != sys->relay_adv_msg.ulRcvNonce )
> + {
> + msg_Err( p_access, "Discovery nonces differ! currNonce:%x rcvd%x",
> sys->glob_ulNonce, ntohl(sys->relay_adv_msg.ulRcvNonce) ); + return
> false;
> + }
> +
> + memcpy( (void*)&sys->relay_adv_msg.ipAddr, &pkt[8], 4 );
> +
> + memset( &sys->relayAddress, 0, sizeof(sys->relayAddress) );
> + sys->relayAddress.sin_family = AF_INET;
> + sys->relayAddress.sin_addr.s_addr = sys->relay_adv_msg.ipAddr;
> + sys->relayAddress.sin_port = htons( AMT_PORT );
> +
> + int nRet = connect( sys->sAMT, (struct sockaddr *)&sys->relayAddress,
> sizeof(sys->relayAddress) ); + if( nRet < 0 )
> + {
> + msg_Err( p_access, "Error connecting AMT UDP socket: %s",
> strerror(errno) ); + return false;
> + }
> +
> + return true;
> +}
> +
> +/**
> + * Receive relay membership query message
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + | V=0 |Type=4 | Reserved |L|G| Response MAC |
> + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
> + | |
> + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + | Request Nonce |
> + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + | |
> + | Encapsulated General Query Message |
> + ~ IPv4:IGMPv3(Membership Query) ~
> + | IPv6:MLDv2(Listener Query) |
> + | |
> + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + | Gateway Port Number | |
> + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
> + | |
> + + +
> + | Gateway IP Address (IPv4 or IPv6) |
> + + +
> + | |
> + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + | |
> + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + */
> +bool amt_rcv_relay_mem_query( access_sys_t *sys, stream_t *p_access )
> +{
> + char pkt[RELAY_QUERY_MSG_LEN];
> + memset( &pkt, 0, RELAY_QUERY_MSG_LEN );
> +
> + struct pollfd ufd[1];
> +
> + ufd[0].fd = sys->sAMT;
> + ufd[0].events = POLLIN;
> +
> + switch( vlc_poll_i11e(ufd, 1, sys->timeout) )
> + {
> + case 0:
> + msg_Err(p_access, "mem query receive time-out");
> + /* fall through */
> + case -1:
> + goto skip;
> + }
> +
> + struct sockaddr tmp;
> + u32 tmp_size = sizeof( struct sockaddr );
> + ssize_t len = recvfrom( sys->sAMT, &pkt, RELAY_QUERY_MSG_LEN, 0, &tmp,
> &tmp_size ); +
> + if (len < 0)
> + {
> + skip:
> + msg_Err(p_access, "length less than zero");
> + return false;
> + }
> +
> + memcpy( (void*)&sys->relay_mem_query_msg.type, &pkt[0], MSG_TYPE_LEN );
> + /* pkt[1] is reserved */
> + memcpy( (void*)&sys->relay_mem_query_msg.uchaMAC[0], &pkt[AMT_HDR_LEN],
> MAC_LEN ); + memcpy( (void*)&sys->relay_mem_query_msg.ulRcvedNonce,
> &pkt[AMT_HDR_LEN + MAC_LEN], NONCE_LEN ); + if(
> sys->relay_mem_query_msg.ulRcvedNonce != sys->glob_ulNonce ) + {
> + msg_Warn( p_access, "Nonces are different rcvd: %x glob: %x",
> sys->relay_mem_query_msg.ulRcvedNonce, sys->glob_ulNonce ); + return
> false;
> + }
> +
> + sys->glob_ulNonce = ntohl(sys->relay_mem_query_msg.ulRcvedNonce);
> +
> + int shift = AMT_HDR_LEN + MAC_LEN + NONCE_LEN;
> + memcpy( (void*)&sys->relay_ip_hdr, (void*)&pkt[shift], IP_HDR_IGMP_LEN
> ); +
> + shift += IP_HDR_IGMP_LEN;
> + sys->relay_igmp_query.type = pkt[shift];
> + shift++;
> + sys->relay_igmp_query.max_resp_code = pkt[shift];
> + shift++;
> + memcpy( (void*)&sys->relay_igmp_query.checksum, &pkt[shift], 2 );
> + shift += 2;
> + memcpy( (void*)&sys->relay_igmp_query.ssmIP, &pkt[shift], 4 );
> + shift += 4;
> + sys->relay_igmp_query.s_qrv = pkt[shift];
> + shift++;
> + if( (int)pkt[shift] == 0 )
> + sys->relay_igmp_query.qqic = 125;
> + else if( (int)pkt[shift] < 128 )
> + sys->relay_igmp_query.qqic = pkt[shift];
> + else {
> + int qqic;
> + qqic = ((pkt[shift]&0x0f) + 0x10) << (((pkt[shift] >> 4)&0x07) +
> 3); + sys->relay_igmp_query.qqic = qqic;
> + }
> +
> + shift++;
> + memcpy( (void*)&sys->relay_igmp_query.nSrc, &pkt[shift], 2 );
> + shift++;
> + memcpy( (void*)&sys->relay_igmp_query.srcIP, &pkt[shift], 4 );
> +
> + return true;
> +}
> +
> +/**
> + * Join SSM group based on input addresses, or use the defaults
> + * */
> +int amt_joinSSM_group( access_sys_t *sys, stream_t *p_access )
> +{
> + struct ip_mreq_source imr;
> + struct sockaddr_in temp;
> + if( inet_pton( AF_INET, sys->mcastGroup, &(temp.sin_addr.s_addr) ) !=
> 1) + {
> + msg_Err( p_access, "Error converting mcast group" );
> + }
> + imr.imr_multiaddr.s_addr = temp.sin_addr.s_addr;
> +
> + inet_pton( AF_INET, sys->srcAddr, &(temp.sin_addr.s_addr) );
> + imr.imr_sourceaddr.s_addr = temp.sin_addr.s_addr;
> + imr.imr_interface.s_addr = INADDR_ANY;
> +
> + return setsockopt( sys->sAMT, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
> (char *)&imr, sizeof(imr) ); +}
> +
> +/**
> + * Leave SSM group that was joined earlier.
> + * */
> +int amt_leaveSSM_group( access_sys_t *sys, stream_t *p_access )
> +{
> + struct ip_mreq_source imr;
> + struct sockaddr_in temp;
> + if( inet_pton( AF_INET, sys->mcastGroup, &(temp.sin_addr.s_addr) ) !=
> 1) + {
> + msg_Err( p_access, "Error converting mcast group" );
> + }
> + imr.imr_multiaddr.s_addr = temp.sin_addr.s_addr;
> +
> + inet_pton( AF_INET, sys->srcAddr, &(temp.sin_addr.s_addr) );
> + imr.imr_sourceaddr.s_addr = temp.sin_addr.s_addr;
> + imr.imr_interface.s_addr = INADDR_ANY;
> +
> + return setsockopt( sys->sAMT, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP,
> (char *)&imr, sizeof(imr) ); +}
> diff --git a/modules/access/amt.h b/modules/access/amt.h
> new file mode 100644
> index 0000000000..924112f78b
> --- /dev/null
> +++ b/modules/access/amt.h
> @@ -0,0 +1,217 @@
> +/**************************************************************************
> *** + * @file amt.h
> + * @brief AMT access module structs
> + *
> + * Some of this code was pulled from or based off Cisco's open-source
> + * SSMAMTtools repo.
> + * Copyright (c) Juniper Networks, Inc., 2018 - 2018. All rights reserved.
> + *
> + * This code is licensed to you under the GNU Lesser General Public License
> + * version 2.1 or later. You may not use this code except in compliance
> with + * the GNU Lesser General Public License.
> + * This code is not an official Juniper product.
> + *
> + * Authors: Natalie Landsberg <nlandsberg at juniper.net>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * as published by the Free Software Foundation; either version 2.1
> + * of the License, or (at your option) any later version.
> + *
> + * This library 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 Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
> USA +
> ***************************************************************************
> */ +
> +/**************************************************************************
> *** + * Various Lengths of Msgs or Hdrs
> +
> ***************************************************************************
> **/ +#define MAC_LEN 6 /* length of generated MAC in bytes */
> +#define NONCE_LEN 4 /* length of nonce in bytes */
> +
> +#define MSG_TYPE_LEN 1 /* length of msg type */
> +#define RELAY_QUERY_MSG_LEN 48 /* total length of relay query */
> +#define RELAY_ADV_MSG_LEN 12 /* length of relay advertisement message
> */ +#define IGMP_QUERY_LEN 24 /* length of encapsulated IGMP query
> message */ +#define AMT_HDR_LEN 2 /* length of AMT header on a
> packet */ +#define IP_HDR_LEN 20 /* length of standard IP header
> */ +#define IP_HDR_IGMP_LEN 24 /* length of IP header with an IGMP
> report */ +#define UDP_HDR_LEN 8 /* length of standard UDP
> header */ +#define AMT_REQUEST_MSG_LEN 9
> +#define AMT_DISCO_MSG_LEN 8
> +
> +/**************************************************************************
> *** + * Different AMT Message Types
> +
> ***************************************************************************
> **/ +#define AMT_RELAY_DISCO 1 /* relay discovery */
> +#define AMT_RELAY_ADV 2 /* relay advertisement */
> +#define AMT_REQUEST 3 /* request */
> +#define AMT_MEM_QUERY 4 /* membership query */
> +#define AMT_MEM_UPD 5 /* membership update */
> +#define AMT_MULT_DATA 6 /* multicast data */
> +#define AMT_TEARDOWN 7 /* teardown (not currently supported) */
> +
> +/**************************************************************************
> *** + * Different IGMP Message Types
> +
> ***************************************************************************
> **/ +#define AMT_IGMPV3_MEMBERSHIP_QUERY_TYPEID 0x11
> +#define AMT_IGMPV3_MEMBERSHIP_REPORT_TYPEID 0x22
> +/* IGMPv2, interoperability */
> +#define AMT_IGMPV1_MEMBERSHIP_REPORT_TYPEID 0x12
> +#define AMT_IGMPV2_MEMBERSHIP_REPORT_TYPEID 0x16
> +#define AMT_IGMPV2_MEMBERSHIP_LEAVE_TYPEID 0x17
> +
> +#define AMT_IGMP_ALLOW 0x05
> +#define AMT_IGMP_BLOCK 0x06
> +
> +#define MCAST_ALLHOSTS "224.0.0.1"
> +
> +typedef struct access_sys_t access_sys_t;
> +
> +typedef char s8;
> +typedef unsigned char u8;
> +typedef short s16;
> +typedef unsigned short u16;
> +typedef int s32;
> +typedef unsigned int u32;
> +
> +typedef struct _amt_ip {
> + u8 ver_ihl;
> + u8 tos;
> + u16 tot_len;
> + u16 id;
> + u16 frag_off;
> + u8 ttl;
> + u8 protocol;
> + u16 check;
> + u32 srcAddr;
> + u32 destAddr;
> +} amt_ip_t;
> +
> +typedef struct _amt_ip_alert {
> + u8 ver_ihl;
> + u8 tos;
> + u16 tot_len;
> + u16 id;
> + u16 frag_off;
> + u8 ttl;
> + u8 protocol;
> + u16 check;
> + u32 srcAddr;
> + u32 destAddr;
> + u32 options;
> +} amt_ip_alert_t;
> +
> +typedef struct _amt_igmpv3_groupRecord {
> + u8 type;
> + u8 auxDatalen;
> + u16 nSrc;
> + u32 ssm;
> + u32 srcIP[1];
> +} amt_igmpv3_groupRecord_t;
> +
> +typedef struct _amt_igmpv3_membership_report {
> + u8 type;
> + u8 resv;
> + u16 checksum;
> + u16 resv2;
> + u16 nGroupRecord;
> + amt_igmpv3_groupRecord_t grp[1];
> +} amt_igmpv3_membership_report_t;
> +
> +typedef struct _amt_igmpv3_membership_query {
> + u8 type;
> + u8 max_resp_code; /* in 100ms, Max Resp Time = (mant | 0x10) << (exp
> + 3) */ + u16 checksum;
> + u32 ssmIP;
> + u8 s_qrv;
> + u8 qqic; /* in second, query Time = (mant | 0x10) << (exp +
> 3) */ + u16 nSrc;
> + u32 srcIP[1];
> +} amt_igmpv3_membership_query_t;
> +
> +typedef struct _amt_membership_update_msg {
> + amt_ip_alert_t ipHead;
> + amt_igmpv3_membership_report_t memReport;
> +} amt_membership_update_msg_t;
> +
> +typedef struct _amt_udpHdr {
> + u16 srcPort;
> + u16 dstPort;
> + u16 len;
> + u16 check;
> +} amt_udpHdr_t;
> +
> +typedef struct _amt_multicast_data {
> + u8 type;
> + u8 resv;
> + amt_ip_t ip;
> + amt_udpHdr_t udp;
> + u8 *buf;
> +} amt_multicast_data_t;
> +
> +typedef struct _argsStruct {
> + access_sys_t *sys;
> + stream_t *p_access;
> +} argsStruct;
> +
> +/* AMT Functions */
> +int amt_sockets_init( access_sys_t *sys, stream_t *p_access );
> +void amt_send_relay_discovery_msg( access_sys_t *sys, stream_t *p_access );
> +void amt_send_relay_request( access_sys_t *sys, stream_t *p_access ); +int
> amt_joinSSM_group( access_sys_t *sys, stream_t *p_access );
> +int amt_leaveSSM_group( access_sys_t *sys, stream_t *p_access );
> +bool amt_rcv_relay_adv( access_sys_t *sys, stream_t *p_access );
> +bool amt_rcv_relay_mem_query( access_sys_t *sys, stream_t *p_access );
> +void amt_send_mem_update( access_sys_t *sys, stream_t *p_access, bool leave
> ); +
> +/* Utility functions */
> +u16 getChecksum( u16 *buffer, int nLen );
> +void makeReport( amt_igmpv3_membership_report_t *mr );
> +void makeIPHeader( amt_ip_alert_t *p_ipHead );
> +
> +struct access_sys_t
> +{
> + int fd;
> + int sAMT;
> + int sQuery;
> + int timeout;
> + int amtTimeout;
> + size_t mtu;
> + bool triedAMT;
> +
> + time_t queryTime;
> +
> + char *mcastGroup;
> + char *srcAddr;
> + char *relayAddr;
> +
> + struct sockaddr_in relayAddrDisco;
> + struct sockaddr_in relayAddress;
> + struct sockaddr_in stLocalAddr;
> + struct sockaddr_in stSvrAddr;
> +
> + u32 glob_ulNonce;
> + u32 ulRelayNonce;
> +
> + struct relay_mem_query_msg_t {
> + u8 type;
> + u32 ulRcvedNonce;
> + u8 uchaMAC[MAC_LEN];
> + u8 uchaIGMP[IGMP_QUERY_LEN];
> + } relay_mem_query_msg;
> +
> + struct relay_adv_msg_t {
> + u8 type;
> + u32 ulRcvNonce;
> + u32 ipAddr;
> + } relay_adv_msg;
> +
> + amt_ip_t relay_ip_hdr;
> + amt_igmpv3_membership_query_t relay_igmp_query;
> +};
--
Rémi Denis-Courmont
http://www.remlab.net/
More information about the vlc-devel
mailing list