[vlc-commits] [Git][videolan/vlc][master] access/amt: Support for IPv6

Steve Lhomme (@robUx4) gitlab at videolan.org
Sat Jan 7 05:39:42 UTC 2023



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
30b938df by Nicolas Zunker at 2023-01-07T05:39:20+00:00
access/amt: Support for IPv6

- - - - -


1 changed file:

- modules/access/amt.c


Changes:

=====================================
modules/access/amt.c
=====================================
@@ -2,7 +2,6 @@
  * @file amt.c
  * @brief Automatic Multicast Tunneling Protocol (AMT) 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) 2018 VLC authors and VideoLAN
  * Copyright (c) Juniper Networks, Inc., 2018. All rights reserved.
@@ -14,6 +13,7 @@
  *          Remi Denis-Courmont                               - original UDP code
  *          Natalie Landsberg <natalie.landsberg97 at gmail.com> - AMT support
  *          Wayne Brassem <wbrassem at rogers.com>               - Added FQDN support
+ *          Nicolas Zunker                                    - Added IPv6 support
  *
  * 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
@@ -79,12 +79,18 @@
 #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 RELAY_QUERY_MSG_LEN 88   /* total length of relay query: 12 byte AMT header, + 36 bytes for IGMPv3 or + 76 for MLDv2 */
+#define RELAY_ADV_MSG_LEN 24     /* length of relay advertisement message: 8 bytes + ip address (4 or 16 bytes) */
 #define IGMP_QUERY_LEN 24        /* length of encapsulated IGMP query message */
 #define IGMP_REPORT_LEN 20
+#define AMT_IPV6_MAX_NUM_SOURCES 10 /* arbitrary maximum length on the number of sources we will read from an MLD Query's address records */
+#define MLD_ADDRESS_RECORD_LEN 36 /* assuming no auxiliary data */
+#define MLD_HEADER_LEN 8
+#define MLD_REPORT_LEN (MLD_HEADER_LEN + MLD_ADDRESS_RECORD_LEN) /* 8 Byte Fixed MLD Header + the Address record (44 bytes)*/
 #define AMT_HDR_LEN 2            /* length of AMT header on a packet */
 #define IP_HDR_LEN 20            /* length of standard IP header */
+#define IPv6_FIXED_HDR_LEN 40
+#define IPv6_HOP_BY_HOP_OPTION_LEN 8 /* For MLD this header is always present */
 #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
@@ -118,12 +124,19 @@
 #define AMT_IGMP_ALLOW 0x05
 #define AMT_IGMP_BLOCK 0x06
 
-#define MCAST_ANYCAST  "0.0.0.0"
+#define AMT_MLD_MCAST_ADDRESS_RECORD_TYPE_ALLOW_NEW_SOURCES 5
+#define AMT_MLD_MCAST_ADDRESS_RECORD_TYPE_BLOCK_OLD_SOURCES 6
+#define AMT_MLD_REPORT_TYPE 143
+#define AMT_MLD_QUERY_TYPE 130
+#define AMT_MLD_DONE_TYPE 132
+
 #define MCAST_ALLHOSTS "224.0.0.22"
-#define LOCAL_LOOPBACK "127.0.0.1"
+#define MCAST_ALL_MLDv2_CAP_ROUTERS "FF02::16"
+#define LINK_SCOPE_ALL_NODES_MCAST "FF02::1"
 #define AMT_PORT 2268
 
-#define DEFAULT_MTU (1500u - (20 + 8))
+#define DEFAULT_MTU (1500u - (IP_HDR_LEN + UDP_HDR_LEN))
+#define DEFAULT_MTU_IPv6 (1500u - (IPv6_FIXED_HDR_LEN + UDP_HDR_LEN))
 
 /* IPv4 Header Format */
 typedef struct _amt_ip {
@@ -139,6 +152,30 @@ typedef struct _amt_ip {
     uint32_t destAddr;
 } amt_ip_t;
 
+typedef struct {
+    uint8_t next_header; /* 58  (ICMPv6) */
+    uint8_t length;
+    /* Router Alert*/
+    uint8_t ra_type; /* 5 */
+    uint8_t ra_length; /* 2 */
+    uint16_t ra_router_alert; /* 0 for MLD */
+    /* Padding Option */
+    uint8_t po_type;
+    uint8_t po_length; /* this should usually be 0 for our purposes */
+} ipv6_hop_by_hop_option_t;
+
+typedef struct {
+    uint8_t version; /* 4 bits, = 6 for ipv6 */
+    uint8_t traffic_class; /* 0 for now */
+    uint32_t flow_label; /* 20 bits, 0 for now */
+    uint16_t payload_len;
+    uint8_t next_header; /* 0 (hop by hop option) */
+    uint8_t hop_limit;
+    struct in6_addr srcAddr;
+    struct in6_addr dstAddr;
+    ipv6_hop_by_hop_option_t hop_by_hop_option;
+} amt_ipv6_t;
+
 /* IPv4 Header Format with options field */
 typedef struct _amt_ip_alert {
     uint8_t  ver_ihl;
@@ -185,23 +222,65 @@ typedef struct _amt_igmpv3_membership_query {
     uint32_t srcIP[1];
 } amt_igmpv3_membership_query_t;
 
+/* MLDv2 Multicast Address Record Format */
+typedef struct {
+    uint8_t record_type;
+    uint8_t aux_data_len; /* length in 32 bit units */
+    uint16_t num_srcs;
+    struct in6_addr mcast_address;
+    struct in6_addr *sources;
+    uint32_t *auxiliary_data;
+} amt_ipv6_multicast_address_record_t;
+
+/* MLDv2 Multicast Listener Query Message */
+typedef struct {
+    uint8_t type; /* 130 */
+    uint8_t code;
+    uint16_t checksum;
+    uint16_t max_resp_code;
+    struct in6_addr mcast_address;
+    bool s_flag;
+    uint8_t qrv; /* 4 bits */
+    uint8_t qqic;
+    uint16_t num_srcs;
+    struct in6_addr *srcs;
+}  amt_mldv2_listener_query_t;
+
+/* MLDv2 Multicast Listener Report Message */
+typedef struct {
+    uint8_t type; /* 143 */
+    uint8_t code;
+    uint16_t checksum;
+    uint16_t num_records;
+    amt_ipv6_multicast_address_record_t records[1];
+}  amt_mldv2_listener_report_t;
+
+/* MLDv1 Multicast Listener Done Message*/
+typedef struct {
+    uint8_t type; /* 132 */
+    uint8_t code;
+    uint16_t checksum;
+    uint16_t max_resp_delay;
+    struct in6_addr mcast_addr;
+} amt_mldv1_listener_done_t;
+
 /* ATM Membership Update Format (RFC7450) */
 typedef struct _amt_membership_update_msg {
     amt_ip_alert_t ipHead;
-    amt_igmpv3_membership_report_t memReport;
+    union {
+        amt_mldv2_listener_report_t mld_memReport;
+        amt_mldv1_listener_done_t mld_listener_done;
+        amt_igmpv3_membership_report_t igmp_memReport;
+    } encapsulated;
 } amt_membership_update_msg_t;
 
 /* AMT Functions */
 static int amt_sockets_init( stream_t *p_access );
 static void amt_send_relay_discovery_msg( stream_t *p_access, char *relay_ip );
 static void amt_send_relay_request( stream_t *p_access, char *relay_ip );
-static int amt_joinSSM_group( stream_t *p_access );
-static int amt_joinASM_group( stream_t *p_access );
-static int amt_leaveASM_group( stream_t *p_access );
-static int amt_leaveSSM_group( stream_t *p_access );
 static bool amt_rcv_relay_adv( stream_t *p_access );
 static bool amt_rcv_relay_mem_query( stream_t *p_access );
-static void amt_send_mem_update( stream_t *p_access, char *relay_ip, bool leave );
+static int amt_send_mem_update( stream_t *p_access, bool leave );
 static bool open_amt_tunnel( stream_t *p_access );
 static void amt_update_timer_cb( void *data );
 
@@ -209,26 +288,37 @@ static void amt_update_timer_cb( void *data );
 typedef struct _access_sys_t
 {
     char *relay;
-    char relayDisco[INET_ADDRSTRLEN];
 
     vlc_timer_t updateTimer;
 
-    /* Mulicast group and source */
-    struct sockaddr_in mcastGroupAddr;
-    struct sockaddr_in mcastSrcAddr;
-
+    /* Mulicast group and source, differentiate between address family based on the sa_family which is the first field (sin_family or sin6_family) in both structs */
+    union {
+        struct sockaddr_in ipv4;
+        struct sockaddr_in6 ipv6;
+    } mcastGroupAddr;
+    union {
+        struct sockaddr_in ipv4;
+        struct sockaddr_in6 ipv6;
+    } mcastSrcAddr;
     /* AMT relay imformation */
-    struct sockaddr_in relayDiscoAddr;
+    union {
+        struct sockaddr_in ipv4;
+        struct sockaddr_in6 ipv6;
+    } relayDiscoAddr;
 
     /* AMT Relay Membership Query data (RFC7450) */
     struct relay_mem_query_msg_t {
         uint32_t ulRcvedNonce;
         uint8_t  type;
         uint8_t  uchaMAC[MAC_LEN];
-        uint8_t  uchaIGMP[IGMP_QUERY_LEN];
+        /* uint8_t  uchaIGMP[IGMP_QUERY_LEN]; */
     } relay_mem_query_msg;
 
-    amt_igmpv3_membership_query_t relay_igmp_query;
+    union {
+        amt_igmpv3_membership_query_t igmp;
+        amt_mldv2_listener_query_t mld;
+    } relay_query;
+
     size_t mtu;
 
     uint32_t glob_ulNonce;
@@ -278,42 +368,39 @@ static int Open( vlc_object_t *p_this )
     stream_t            *p_access = (stream_t*) p_this;
     access_sys_t        *sys = NULL;
     struct addrinfo      hints, *serverinfo = NULL;
-    struct sockaddr_in  *server_addr;
-    char                *psz_name = NULL, *saveptr, *psz_strtok_r;
-    char                 mcastSrc_buf[INET_ADDRSTRLEN], mcastGroup_buf[INET_ADDRSTRLEN];
+    char                *saveptr, *psz_strtok_r;
+    char                 mcastSrc_buf[INET6_ADDRSTRLEN], mcastGroup_buf[INET6_ADDRSTRLEN];
     const char          *mcastSrc, *mcastGroup;
     int                  i_bind_port = 1234, i_server_port = 0, VLC_ret = VLC_SUCCESS, response;
     vlc_url_t            url = { 0 };
 
     if( p_access->b_preparsing )
+    {
         return VLC_EGENERIC;
+    }
 
     /* Set up p_access */
     ACCESS_SET_CALLBACKS( NULL, BlockAMT, Control, NULL );
 
     if( !p_access->psz_location )
+    {
         return VLC_EGENERIC;
+    }
 
     /* Allocate the structure for holding AMT info and zeroize it */
     sys = vlc_obj_calloc( p_this, 1, sizeof( *sys ) );
     if( unlikely( sys == NULL ) )
+    {
         return VLC_ENOMEM;
+    }
 
-    /* The standard MPEG-2 transport is 188 bytes.  7 packets fit into a standard 1500 byte Ethernet frame */
-    sys->mtu = 7 * 188;
+    sys->mtu = 1500;
 
     p_access->p_sys = sys;
 
     sys->fd = sys->sAMT = sys->sQuery = -1;
 
-    psz_name = strdup( p_access->psz_location );
-    if ( unlikely( psz_name == NULL ) )
-    {
-        VLC_ret = VLC_EGENERIC;
-        goto cleanup;
-    }
-
-    /* Parse psz_name syntax :
+    /* Parse psz_url syntax :
      * [serveraddr[:serverport]][@[bindaddr]:[bindport]] */
     if( vlc_UrlParse( &url, p_access->psz_url ) != 0 )
     {
@@ -346,13 +433,16 @@ static int Open( vlc_object_t *p_this )
 
     /* If UDP port provided then assign port to stream */
     if( url.i_port > 0 )
+    {
+        msg_Dbg( p_access, "Parsed AMT URL port %d",url.i_port);
         i_bind_port = url.i_port;
-
+    }
+    
     msg_Dbg( p_access, "Opening multicast: %s:%d local=%s:%d", url.psz_host, i_server_port, url.psz_path, i_bind_port );
 
     /* Initialize hints prior to call to vlc_getaddrinfo with either IP address or FQDN */
     memset( &hints, 0, sizeof( hints ));
-    hints.ai_family = AF_INET;  /* Setting to AF_UNSPEC accepts both IPv4 and IPv6 */
+    hints.ai_family = AF_UNSPEC;  /* Setting to AF_UNSPEC accepts both IPv4 and IPv6 */
     hints.ai_socktype = SOCK_DGRAM;
 
     /* Retrieve list of multicast addresses matching the multicast group identifier */
@@ -367,18 +457,33 @@ static int Open( vlc_object_t *p_this )
     }
 
     /* Convert binary socket address to string */
-    server_addr = (struct sockaddr_in *) serverinfo->ai_addr;
-    if( unlikely( inet_ntop(AF_INET, &(server_addr->sin_addr), mcastGroup_buf, INET_ADDRSTRLEN) == NULL ) )
+    void *binary_address = 0;
+    if (serverinfo->ai_family == AF_INET)
+    {
+        binary_address = &( (struct sockaddr_in*) serverinfo->ai_addr)->sin_addr;
+    }
+    else
+    {
+        binary_address = &( (struct sockaddr_in6*) serverinfo->ai_addr)->sin6_addr;
+    }
+    if( unlikely( inet_ntop(serverinfo->ai_family, binary_address, mcastGroup_buf, INET6_ADDRSTRLEN) == NULL ) )
     {
         int errConv = errno;
         msg_Err(p_access, "Could not convert binary socket address to string: %s", gai_strerror(errConv));
         goto cleanup;
     }
     mcastGroup = mcastGroup_buf;
-
     /* Store the binary socket representation of multicast group address */
-    sys->mcastGroupAddr = *server_addr;
-
+    if ( serverinfo->ai_family == AF_INET )
+    {
+        sys->mcastGroupAddr.ipv4 = *(struct sockaddr_in*)serverinfo->ai_addr;
+        hints.ai_family = AF_INET; /* now that we know family of the group we can improve the hints for resolving the source */
+    }
+    else
+    {
+        sys->mcastGroupAddr.ipv6 = *(struct sockaddr_in6*)serverinfo->ai_addr;
+        hints.ai_family = AF_INET6;
+    }
     /* Release the allocated memory */
     freeaddrinfo( serverinfo );
     serverinfo = NULL;
@@ -388,10 +493,10 @@ static int Open( vlc_object_t *p_this )
     msg_Dbg( p_access, "Setting multicast group address to %s", mcastGroup);
 
     /* Extract the source from the URL, or the multicast group when no source is provided */
-    psz_strtok_r = strtok_r( psz_name, "@", &saveptr );
+    psz_strtok_r = strtok_r( (char *) p_access->psz_location, "@", &saveptr );
     if ( !psz_strtok_r )
     {
-        msg_Err( p_access, "Could not parse location %s", psz_name);
+        msg_Err( p_access, "Could not parse location %s", p_access->psz_location);
         VLC_ret = VLC_EGENERIC;
         goto cleanup;
     }
@@ -399,39 +504,54 @@ static int Open( vlc_object_t *p_this )
     /* Store the string representation */
     mcastSrc = psz_strtok_r;
 
-    /* If strings are equal then no multicast source has been specified, so try anycast */
-    if( strcmp( url.psz_host, mcastSrc ) == 0 )
+    /* If IP addresses in the strings are equal then no multicast source has been specified, so try anycast */
+    /* The mcastSrc URL may include a port and may have the brackets of an IPv6 address whereas psz_host is just the IP address so we use strstr() instead of strcmp */
+    if( strstr( mcastSrc, url.psz_host ) )
     {
-        mcastSrc = MCAST_ANYCAST;
-        sys->mcastSrcAddr.sin_addr.s_addr = 0;
+        mcastSrc = 0; // for net_OpenDgram()
+        memset(&sys->mcastSrcAddr,0,sizeof(sys->mcastSrcAddr));
         msg_Dbg( p_access, "No multicast source address specified, trying ASM...");
     }
-
-    /* retrieve list of source addresses matching the multicast source identifier */
-    response = vlc_getaddrinfo( mcastSrc, AMT_PORT, &hints, &serverinfo );
-
-    /* If an error returned print reason and exit */
-    if( response != 0 )
+    else
     {
-        msg_Err( p_access, "Could not find multicast source %s, reason: %s", mcastSrc, gai_strerror(response) );
-        VLC_ret = VLC_EGENERIC;
-        goto cleanup;
-    }
+        /* retrieve list of source addresses matching the multicast source identifier */
+        response = vlc_getaddrinfo( mcastSrc, AMT_PORT, &hints, &serverinfo );
 
-    /* Convert binary socket address to string */
-    server_addr = (struct sockaddr_in *) serverinfo->ai_addr;
-    if( unlikely( inet_ntop(AF_INET, &(server_addr->sin_addr), mcastSrc_buf, INET_ADDRSTRLEN) == NULL ) )
-    {
-        int errConv = errno;
-        msg_Err(p_access, "Could not binary socket address to string: %s", gai_strerror(errConv));
-        goto cleanup;
-    }
-    mcastSrc = mcastSrc_buf;
+        /* If an error returned print reason and exit */
+        if( response != 0 )
+        {
+            msg_Err( p_access, "Could not find multicast source %s, reason: %s", mcastSrc, gai_strerror(response) );
+            VLC_ret = VLC_EGENERIC;
+            goto cleanup;
+        }
 
-    /* Store the binary socket representation of multicast source address */
-    sys->mcastSrcAddr = *server_addr;
+        /* Convert binary socket address to string */
+        binary_address = 0;
+        if (serverinfo->ai_family == AF_INET)
+        {
+            binary_address = &( (struct sockaddr_in*) serverinfo->ai_addr)->sin_addr;
+        }
+        else
+        {
+            binary_address = &( (struct sockaddr_in6*) serverinfo->ai_addr)->sin6_addr;
+        }
+        if( unlikely( inet_ntop(serverinfo->ai_family, binary_address, mcastSrc_buf, INET6_ADDRSTRLEN) == NULL ) )
+        {
+            int errConv = errno;
+            msg_Err(p_access, "Could not convert binary socket address to string: %s", gai_strerror(errConv));
+            goto cleanup;
+        }
+        mcastSrc = mcastSrc_buf;
+        /* Store the binary socket representation of multicast source address */
+        if ( serverinfo->ai_family == AF_INET )
+        {
+            sys->mcastSrcAddr.ipv4 = *(struct sockaddr_in*)serverinfo->ai_addr;
+        } else {
+            sys->mcastSrcAddr.ipv6 = *(struct sockaddr_in6*)serverinfo->ai_addr;
+        }
+        msg_Dbg( p_access, "Setting multicast source address to %s", mcastSrc);
+    }
 
-    msg_Dbg( p_access, "Setting multicast source address to %s", mcastSrc);
 
     /* Pull the AMT relay address from the settings */
     sys->relay = var_InheritString( p_access, "amt-relay" );
@@ -469,7 +589,6 @@ static int Open( vlc_object_t *p_this )
 
 cleanup: /* fall through */
 
-    free( psz_name );
     vlc_UrlClean( &url );
     if( serverinfo )
         freeaddrinfo( serverinfo );
@@ -497,14 +616,8 @@ static void Close( vlc_object_t *p_this )
     /* If using AMT tunneling send leave message and free the relay addresses */
     if ( sys->tryAMT )
     {
-        /* Prepare socket options */
-        if( sys->mcastSrcAddr.sin_addr.s_addr )
-            amt_leaveSSM_group( p_access );
-        else
-            amt_leaveASM_group( p_access );
-
         /* Send IGMP leave message */
-        amt_send_mem_update( p_access, sys->relayDisco, true );
+        amt_send_mem_update( p_access, true );
     }
     free( sys->relay );
 
@@ -550,12 +663,15 @@ static int Control( stream_t *p_access, int i_query, va_list args )
 static block_t *BlockAMT(stream_t *p_access, bool *restrict eof)
 {
     access_sys_t *sys = p_access->p_sys;
-    ssize_t len = 0, shift = 0, tunnel = IP_HDR_LEN + UDP_HDR_LEN + AMT_HDR_LEN;
+    ssize_t len = 0, shift = 0;
+    int tunnel = UDP_HDR_LEN + AMT_HDR_LEN + (sys->mcastGroupAddr.ipv4.sin_family == AF_INET ? IP_HDR_LEN : IPv6_FIXED_HDR_LEN );
 
     /* Allocate anticipated MTU buffer for holding the UDP packet suitable for native or AMT tunneled multicast */
     block_t *pkt = block_Alloc( sys->mtu + tunnel );
     if ( unlikely( pkt == NULL ) )
+    {
         return NULL;
+    }
 
     struct pollfd ufd[1];
 
@@ -587,41 +703,136 @@ static block_t *BlockAMT(stream_t *p_access, bool *restrict eof)
     /* If using AMT tunneling perform basic checks and point to beginning of the payload */
     if( sys->tryAMT )
     {
+
         /* AMT is a wrapper for UDP streams, so recv is used. */
         len = recv( sys->sAMT, pkt->p_buffer, sys->mtu + tunnel, 0 );
 
         /* Check for the integrity of the received AMT packet */
-        if( len < 0 || *(pkt->p_buffer) != AMT_MULT_DATA )
+        if( len < tunnel || *(pkt->p_buffer) != AMT_MULT_DATA )
+        {
+            msg_Err(p_access, "Error pulling data from AMT socket. Length of data : %zd may be too short (should be minimum %d) - AMT header is %s",len, tunnel, (len > 0 && pkt->p_buffer[0] == AMT_MULT_DATA) ? "correct" : "incorrect" );
             goto error;
+        }
+
+        uint16_t payload_len = 0;
+        bool b_is_fragmented = 0;
+        static uint32_t fragment_id = 0;
+
+        /* determine the payload length from the IP header(s), and handle fragmentation if it occurs */
+        int i_ipv = pkt->p_buffer[AMT_HDR_LEN] >> 4;
+        if( i_ipv == 4 )
+        {
+            payload_len = pkt->p_buffer[AMT_HDR_LEN + 2] << 8;
+            payload_len += pkt->p_buffer[AMT_HDR_LEN + 3];
+            /* in ipv4 headers its the total length so we ned to subtract the ip header's length (bottom 4 bits of the first byte) */
+            payload_len -= 4 * (pkt->p_buffer[AMT_HDR_LEN] & 0x0F);
+
+        }
+        else if ( i_ipv == 6 )
+        {
+            payload_len = pkt->p_buffer[AMT_HDR_LEN + 4] << 8;
+            payload_len += pkt->p_buffer[AMT_HDR_LEN + 5];
+
+            int i_next_hdr = pkt->p_buffer[AMT_HDR_LEN + 6];
+            b_is_fragmented = (i_next_hdr == 44);
+            if ( !b_is_fragmented && i_next_hdr != 17 )
+            {
+                msg_Warn(p_access, "Received unexpected ip header in tunneled AMT data: next header = %d",i_next_hdr);
+                /* we can probably survive this (provided there is no fragmentation) since we calculate the length as the difference between the length of the received data and the length listed in the ip header */
+            }
+
+            if ( b_is_fragmented && len < tunnel + 8 )
+            {
+                msg_Err(p_access, "Received length of data %zd is smaller than minimum length for an ip header + fragmentation header (%d)",len, tunnel + 8);
+                goto error;
+            }
+
+            uint32_t tmp = 0;
+            memcpy(&tmp,&pkt->p_buffer[AMT_HDR_LEN + IPv6_FIXED_HDR_LEN + 4],4);
+            if ( b_is_fragmented && !fragment_id )
+            {
+                /* this is the start of a fragment, payload len in the ipv6 header will be wrong */
+                payload_len = len - (tunnel + 8);
+
+                fragment_id = tmp;
+
+                i_next_hdr = pkt->p_buffer[AMT_HDR_LEN + IPv6_FIXED_HDR_LEN];
+                if ( i_next_hdr != 17 )
+                {
+                    msg_Err(p_access, "Received unexpected next header in ipv6 fragment header: next header = %d",i_next_hdr);
+                    goto error; /* here we actually need to be sure the next header is UDP othewrise we cant know the payload length */
+                }
+
+                msg_Dbg(p_access, "Start of new fragment, id is 0x%x (NETWORK BYTE ORDER) , first fragment's length is %u (%zd total - %d tunnel size) (mtu is %zd)",fragment_id, payload_len,len,tunnel,sys->mtu);
+            }
+            else if ( b_is_fragmented && fragment_id != tmp )
+            {
+                msg_Warn(p_access, "Received fragment id does not match last seen fragment id : 0x%x expected vs 0x%x received",fragment_id,tmp);
+                payload_len -= 8; /* still try to receive it */
+            }
+            else if ( b_is_fragmented )
+            {
+                /* this is a subsequent fragment, the payload len in the ipv6 header is right, just need to subtract fragmentation header length */
+                payload_len -= 8;
+            }
+
+            if ( b_is_fragmented && (pkt->p_buffer[AMT_HDR_LEN + IPv6_FIXED_HDR_LEN + 3] & 1) == 0 )
+            {
+                fragment_id = 0; /* no more fragments */
+            }
+
+
+
+        }
+        else
+        {
+            msg_Err(p_access, "Received unexpected ip version in tunneled AMT data: %d",i_ipv);
+            goto error;
+        }
+
+        /* if its fragmented, payload_len will be set correctly before we get here */
+        if ( !b_is_fragmented )
+        {
+            payload_len -= UDP_HDR_LEN;
+        }
 
         /* Set the offet to the first byte of the payload */
-        shift += tunnel;
+        shift = len - payload_len;
+
+        if ( !payload_len || (shift < tunnel && !b_is_fragmented) )
+        {
+            msg_Err(p_access, "Length listed in the ip header is unrealistic: %zd bytes received but %u were listed in ip header. Tunnel overhead should be %d bytes",len,payload_len,tunnel);
+            goto error;
+        }
+
 
         /* If the length received is less than the AMT tunnel header then it's truncated */
         if( len < tunnel )
         {
             msg_Err(p_access, "%zd bytes packet truncated (MTU was %zd)", len, sys->mtu);
             pkt->i_flags |= BLOCK_FLAG_CORRUPTED;
-        }
-
+        } else {
         /* Otherwise subtract the length of the AMT encapsulation from the packet received */
-        else
-        {
-            len -= tunnel;
+            len -= shift;
         }
-    }
+
+    } else {
     /* Otherwise pull native multicast */
-    else
-    {
         struct sockaddr temp;
         socklen_t temp_size = sizeof( struct sockaddr );
-        len = recvfrom( sys->sAMT, (char *)pkt->p_buffer, sys->mtu + tunnel, 0, (struct sockaddr*)&temp, &temp_size );
+        len = recvfrom( sys->fd, (char *)pkt->p_buffer, sys->mtu + tunnel, 0, (struct sockaddr*)&temp, &temp_size );
+        if ( len <= 0 )
+        {
+            msg_Err(p_access, "recv() call failed: %zd was returned", len);
+            goto error;
+        }
     }
 
     /* Set the offset to payload start */
     pkt->p_buffer += shift;
-    pkt->i_buffer -= shift;
+    pkt->i_buffer = len;
 
+    msg_Dbg(p_access, "Received mcast/amt packet of length %zd",len);
     return pkt;
 
 error:
@@ -638,7 +849,7 @@ static bool open_amt_tunnel( stream_t *p_access )
     access_sys_t *sys = p_access->p_sys;
 
     memset( &hints, 0, sizeof( hints ));
-    hints.ai_family = AF_INET;  /* Setting to AF_UNSPEC accepts both IPv4 and IPv6 */
+    hints.ai_family = AF_UNSPEC;  /* Setting to AF_UNSPEC accepts both IPv4 and IPv6 */
     hints.ai_socktype = SOCK_DGRAM;
 
     msg_Dbg( p_access, "Attempting AMT to %s...", sys->relay);
@@ -657,31 +868,40 @@ static bool open_amt_tunnel( stream_t *p_access )
     /* Iterate through the list of sockets to find one that works */
     for (server = serverinfo; server != NULL && !vlc_killed(); server = server->ai_next)
     {
-        struct sockaddr_in *server_addr = (struct sockaddr_in *) server->ai_addr;
-        char relay_ip[INET_ADDRSTRLEN];
+        struct sockaddr *server_addr = server->ai_addr; /* can be either an ipv4 struct sockaddr_in or a ipv6 struct sockaddr_in6 */
+        char relay_ip[INET6_ADDRSTRLEN];
 
         /* Convert to binary representation */
-        if( unlikely( inet_ntop(AF_INET, &(server_addr->sin_addr), relay_ip, INET_ADDRSTRLEN) == NULL ) )
+        void *binary_address = 0;
+        if (server->ai_family == AF_INET)
         {
-            int errConv = errno;
-            msg_Err(p_access, "Could not convert relay ip to binary representation: %s", gai_strerror(errConv));
-            goto error;
+            binary_address = &( (struct sockaddr_in*) server->ai_addr)->sin_addr;
         }
-
-        /* Store string representation */
-        memcpy(sys->relayDisco, relay_ip, INET_ADDRSTRLEN);
-        if( unlikely( sys->relayDisco == NULL ) )
+        else
+        {
+            binary_address = &( (struct sockaddr_in6*) server->ai_addr)->sin6_addr;
+        }
+        if( unlikely( inet_ntop(server->ai_family, binary_address, relay_ip,INET6_ADDRSTRLEN) == NULL ) )
         {
+            int i_errConv = errno;
+            msg_Err(p_access, "Could not convert relay ip to binary representation: %s", gai_strerror(i_errConv));
             goto error;
         }
 
-        msg_Dbg( p_access, "Trying AMT Server: %s", sys->relayDisco);
+        msg_Dbg( p_access, "Trying AMT Server: %s", relay_ip);
 
         /* Store the binary representation */
-        sys->relayDiscoAddr.sin_addr = server_addr->sin_addr;
+        if ( server->ai_family == AF_INET )
+        {
+            sys->relayDiscoAddr.ipv4 = *(struct sockaddr_in*)server_addr;
+        } else {
+            sys->relayDiscoAddr.ipv6 = *(struct sockaddr_in6*)server_addr;
+        }
 
         if( amt_sockets_init( p_access ) != 0 )
+        {
             continue; /* Try next server */
+        }
 
         /* Negotiate with AMT relay and confirm you can pull a UDP packet  */
         amt_send_relay_discovery_msg( p_access, relay_ip );
@@ -704,32 +924,7 @@ static bool open_amt_tunnel( stream_t *p_access )
         }
         msg_Dbg( p_access, "Received AMT relay membership query from %s", relay_ip );
 
-        /* If single source multicast send SSM join */
-        if( sys->mcastSrcAddr.sin_addr.s_addr )
-        {
-            if( amt_joinSSM_group( p_access ) != 0 )
-            {
-                msg_Err( p_access, "Error joining SSM %s", vlc_strerror(errno) );
-                goto error;
-            }
-            msg_Dbg( p_access, "Joined SSM" );
-        }
-
-        /* If any source multicast send ASM join */
-        else {
-            if( amt_joinASM_group( p_access ) != 0 )
-            {
-                msg_Err( p_access, "Error joining ASM %s", vlc_strerror(errno) );
-                goto error;
-            }
-            msg_Dbg( p_access, "Joined ASM group" );
-        }
-
-        /* If started, the timer must be stopped before trying the next server
-         * in order to avoid data-race with sys->sAMT. */
-        vlc_timer_disarm( sys->updateTimer );
-
-        amt_send_mem_update( p_access, sys->relayDisco, false );
+        amt_send_mem_update( p_access, false );
         bool eof=false;
         block_t *pkt;
 
@@ -746,7 +941,8 @@ static bool open_amt_tunnel( stream_t *p_access )
 
             /* Arm IGMP timer once we've confirmed we are getting packets */
             vlc_timer_schedule( sys->updateTimer, false,
-                        VLC_TICK_FROM_SEC( sys->relay_igmp_query.qqic ), VLC_TICK_FROM_SEC( sys->relay_igmp_query.qqic ) );
+                        VLC_TICK_FROM_SEC( sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? sys->relay_query.igmp.qqic : sys->relay_query.mld.qqic), 0 );
+            msg_Err(p_access, "Arming timer. qqic is %d",sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? sys->relay_query.igmp.qqic : sys->relay_query.mld.qqic);
 
             break;   /* found an active server sending UDP packets, so exit loop */
         }
@@ -766,10 +962,60 @@ static bool open_amt_tunnel( stream_t *p_access )
 error:
     vlc_timer_disarm( sys->updateTimer );
     if( serverinfo )
+    {
         freeaddrinfo( serverinfo );
+    }
     return false;
 }
 
+/**
+ * Calculate checksum
+ * */
+static unsigned short ipv6_checksum(unsigned short *buffer, int nLen)
+{
+    int i_nleft = nLen;
+    int i_sum = 0;
+    unsigned short *w = buffer;
+    unsigned short answer = 0;
+
+    uint8_t pseudo_header [40];
+    memset(pseudo_header,0,40);
+
+    pseudo_header[16] = 0xFF;
+    pseudo_header[17] = 0x02;
+    pseudo_header[31] = 0x16;
+
+    uint32_t tmp = htonl(nLen); /* MLD report length */
+    memcpy(&pseudo_header[32],&tmp,4);
+    pseudo_header[39] = 58; /* next header = ICMPv6 */
+
+    unsigned short *buf1 = (unsigned short*) pseudo_header;
+
+    for( int i = 0; i < 20; i++ )
+    {
+        i_sum += *buf1++;
+    }
+
+    while ( i_nleft > 1)
+    {
+        i_sum += *w++;
+        i_nleft -= 2;
+    }
+    if ( i_nleft == 1)
+    {
+        *(unsigned char*)(&answer) = *(unsigned char*)w;
+        i_sum += answer;
+    }
+
+    while ( i_sum>>16 )
+    {
+        i_sum = (i_sum & 0xffff) + (i_sum >> 16);
+    }
+
+    answer = ~i_sum;
+    return answer;
+}
+
 /**
  * Calculate checksum
  * */
@@ -790,11 +1036,40 @@ static unsigned short get_checksum( unsigned short *buffer, int nLen )
         *(unsigned char*)(&answer) = *(unsigned char*)w;
         sum += answer;
     }
-    sum = (sum >> 16) + (sum & 0xffff);
+
+    while ( sum>>16 )
+    {
+        sum = (sum & 0xffff) + (sum >> 16);
+    }
+
     answer = ~sum;
-    return (answer);
+    return answer;
 }
 
+/**
+ * Make MLD Listener report
+ * */
+static int make_mld_report( stream_t *p_access, bool leave, amt_mldv2_listener_report_t *report, struct in6_addr *group, struct in6_addr *src)
+{
+
+    report->type = AMT_MLD_REPORT_TYPE;
+    report->code = 0;
+    report->checksum = 0;
+    report->num_records = 1;
+    report->records[0].record_type = leave ? AMT_MLD_MCAST_ADDRESS_RECORD_TYPE_BLOCK_OLD_SOURCES : AMT_MLD_MCAST_ADDRESS_RECORD_TYPE_ALLOW_NEW_SOURCES;
+    report->records[0].aux_data_len = 0;
+    report->records[0].num_srcs = 1;
+    report->records[0].mcast_address = *group;
+    report->records[0].sources = vlc_obj_calloc( p_access, 1, 16); /* ipv6 addr */
+    if ( !report->records[0].sources )
+    {
+        return 1;
+    }
+    report->records[0].sources[0] = *src; /* ipv6 addr */
+    return 0;
+}
+
+
 /**
  * Make IGMP Membership report
  * */
@@ -821,7 +1096,33 @@ static void make_ip_header( amt_ip_alert_t *p_ipHead )
     p_ipHead->protocol = 0x02;
     p_ipHead->check = 0;
     p_ipHead->srcAddr = INADDR_ANY;
-    p_ipHead->options = 0x0000;
+    p_ipHead->options = 0x0;
+}
+
+/**
+ * Make IPv6 Packet structure preceding an MLD message
+ * */
+static void make_ipv6( amt_ipv6_t *p, uint16_t length, struct in6_addr *dst )
+{
+    p->version = 6;
+    p->traffic_class = 0;
+    p->flow_label = 0;
+    p->payload_len = length;
+    p->next_header = 0; /* hop by hop option */
+    p->hop_limit = 1;
+    p->srcAddr = (struct in6_addr) IN6ADDR_ANY_INIT;
+    p->dstAddr = *dst;
+
+    ipv6_hop_by_hop_option_t hbh;
+    hbh.next_header = 58; /* ICMPv6 */
+    hbh.length = 0;
+    hbh.ra_type = 5;
+    hbh.ra_length = 2;
+    hbh.ra_router_alert = 0;
+    hbh.po_type = 1; /* skip and continue, with low order bits set */
+    hbh.po_length = 0;
+
+    p->hop_by_hop_option = hbh;
 }
 
 /** Create relay discovery socket, query socket, UDP socket and
@@ -830,17 +1131,16 @@ static void make_ip_header( amt_ip_alert_t *p_ipHead )
  */
 static int amt_sockets_init( stream_t *p_access )
 {
-    struct sockaddr_in rcvAddr;
+    struct sockaddr *rcvAddr;
+    struct sockaddr_in rcvAddr4;
+    struct sockaddr_in6 rcvAddr6;
     access_sys_t *sys = p_access->p_sys;
-    memset( &rcvAddr, 0, sizeof(struct sockaddr_in) );
+    memset( &rcvAddr4, 0, sizeof(struct sockaddr_in) );
+    memset( &rcvAddr6, 0, sizeof(struct sockaddr_in6) );
     int enable = 0, res = 0;
 
-    /* Relay anycast address for discovery */
-    sys->relayDiscoAddr.sin_family = AF_INET;
-    sys->relayDiscoAddr.sin_port = htons( AMT_PORT );
-
     /* create UDP socket */
-    sys->sAMT = vlc_socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP, true );
+    sys->sAMT = vlc_socket( sys->relayDiscoAddr.ipv4.sin_family, SOCK_DGRAM, IPPROTO_UDP, true );
     if( sys->sAMT == -1 )
     {
         msg_Err( p_access, "Failed to create UDP socket" );
@@ -854,17 +1154,23 @@ static int amt_sockets_init( stream_t *p_access )
         goto error;
     }
 
-    rcvAddr.sin_family      = AF_INET;
-    rcvAddr.sin_port        = htons( 0 );
-    rcvAddr.sin_addr.s_addr = INADDR_ANY;
+    rcvAddr4.sin_family      = AF_INET;
+    rcvAddr4.sin_port        = htons( 0 );
+    rcvAddr4.sin_addr.s_addr = INADDR_ANY;
 
-    if( bind(sys->sAMT, (struct sockaddr *)&rcvAddr, sizeof(rcvAddr) ) != 0 )
+    rcvAddr6.sin6_family      = AF_INET6;
+    rcvAddr6.sin6_port        = htons( 0 );
+    rcvAddr6.sin6_addr = (struct in6_addr) IN6ADDR_ANY_INIT;
+
+    rcvAddr = sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? (struct sockaddr*) &rcvAddr4 : (struct sockaddr*) &rcvAddr6;
+
+    if( bind(sys->sAMT, rcvAddr, sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? sizeof(rcvAddr4) : sizeof(rcvAddr6) ) != 0 )
     {
         msg_Err( p_access, "Failed to bind UDP socket error: %s", vlc_strerror(errno) );
         goto error;
     }
 
-    sys->sQuery = vlc_socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP, true );
+    sys->sQuery = vlc_socket( sys->relayDiscoAddr.ipv4.sin_family, SOCK_DGRAM, IPPROTO_UDP, true );
     if( sys->sQuery == -1 )
     {
         msg_Err( p_access, "Failed to create query socket" );
@@ -872,14 +1178,22 @@ static int amt_sockets_init( stream_t *p_access )
     }
 
     /* bind socket to local address */
-    struct sockaddr_in stLocalAddr =
+    struct sockaddr_in stLocalAddr4 =
     {
         .sin_family      = AF_INET,
         .sin_port        = htons( 0 ),
         .sin_addr.s_addr = INADDR_ANY,
     };
 
-    if( bind(sys->sQuery, (struct sockaddr *)&stLocalAddr, sizeof(struct sockaddr) ) != 0 )
+    struct sockaddr_in6 stLocalAddr6;
+    memset( &rcvAddr6, 0, sizeof(struct sockaddr_in6) );
+    stLocalAddr6.sin6_family = AF_INET6;
+    stLocalAddr6.sin6_port = htons( 0 );
+    stLocalAddr6.sin6_addr = (struct in6_addr) IN6ADDR_ANY_INIT;
+
+    struct sockaddr *stLocalAddr = sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? (struct sockaddr*) &stLocalAddr4 : (struct sockaddr*) &stLocalAddr6;
+
+    if( bind(sys->sQuery, stLocalAddr, sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? sizeof(stLocalAddr4) : sizeof(stLocalAddr6) ) != 0 )
     {
         msg_Err( p_access, "Failed to bind query socket" );
         goto error;
@@ -935,11 +1249,12 @@ static void amt_send_relay_discovery_msg( stream_t *p_access, char *relay_ip )
     sys->glob_ulNonce = ulNonce;
 
     /* send it */
-    nRet = sendto( sys->sAMT, chaSendBuffer, sizeof(chaSendBuffer), 0,\
-            (struct sockaddr *)&sys->relayDiscoAddr, sizeof(struct sockaddr) );
+    nRet = sendto( sys->sAMT, chaSendBuffer, sizeof(chaSendBuffer), 0, (struct sockaddr*) &sys->relayDiscoAddr, sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
 
     if( nRet < 0)
+    {
         msg_Err( p_access, "Sendto failed to %s with error: %s.", relay_ip, vlc_strerror_c(errno));
+    }
 }
 
 /**
@@ -980,7 +1295,7 @@ static void amt_send_relay_request( stream_t *p_access, char *relay_ip )
      */
 
     chaSendBuffer[0] = AMT_REQUEST;
-    chaSendBuffer[1] = 0;
+    chaSendBuffer[1] = sys->mcastGroupAddr.ipv4.sin_family == AF_INET6;
     chaSendBuffer[2] = 0;
     chaSendBuffer[3] = 0;
 
@@ -990,7 +1305,94 @@ static void amt_send_relay_request( stream_t *p_access, char *relay_ip )
     nRet = send( sys->sAMT, chaSendBuffer, sizeof(chaSendBuffer), 0 );
 
     if( nRet < 0 )
+    {
         msg_Err(p_access, "Error sending relay request to %s error: %s", relay_ip, vlc_strerror(errno) );
+    }
+}
+
+
+/**
+ * @brief Serialize an IPv6 Packet
+ *
+ * Params: the packet to serialize, a buffer to write it into and the maximum length of that buffer
+ * Returns: the number of bytes written on success, else 0
+ */
+
+static int serialize_ipv6_pkt(amt_ipv6_t *ip, uint8_t *buf, int buflen){
+    if ( buflen < IPv6_HOP_BY_HOP_OPTION_LEN + IPv6_FIXED_HDR_LEN )
+    {
+        return 0;
+    }
+
+    int i = 0;
+
+    buf[i++] = (ip->version << 4) | (ip->traffic_class >> 4); /* first 4 bytes of tc */
+    buf[i++] = (ip->traffic_class & 0xF) | ((ip->flow_label >> 16) & 0xF); /* bottom four bits of tc + "upper" 4 of flow_label (flow label is only 20 bits) */
+    buf[i++] = ((ip->flow_label >> 8) & 0xFF);
+    buf[i++] = (ip->flow_label & 0xFF);
+
+    buf[i++] = ip->payload_len >> 16;
+    buf[i++] = ip->payload_len & 0xFF;
+    buf[i++] = ip->next_header;
+    buf[i++] = ip->hop_limit;
+
+    memcpy(&buf[i],&ip->srcAddr,16);
+    i+= 16;
+    memcpy(&buf[i],&ip->dstAddr,16);
+    i+= 16;
+
+    buf[i++] = ip->hop_by_hop_option.next_header;
+    buf[i++] = ip->hop_by_hop_option.length;
+    buf[i++] = ip->hop_by_hop_option.ra_type;
+    buf[i++] = ip->hop_by_hop_option.ra_length;
+    buf[i++] = ip->hop_by_hop_option.ra_router_alert >> 8;
+    buf[i++] = ip->hop_by_hop_option.ra_router_alert & 0xFF;
+    buf[i++] = ip->hop_by_hop_option.po_type;
+    buf[i++] = ip->hop_by_hop_option.po_length;
+
+    return i;
+}
+
+/**
+ * @brief Serializes an MLD Report - computes and inserts checksum as well
+ *
+ * Params: report to serialize with an empty checksum, a buffer to write it into and the maximum length of that buffer
+ * Returns: the number of bytes written on success, else 0
+ */
+
+static int serialize_mld_report(amt_mldv2_listener_report_t *report, uint8_t *buf, int buflen, stream_t *p_access){
+    if ( buflen < MLD_REPORT_LEN )
+    {
+        return 0;
+    }
+
+    uint16_t tmp;
+    int i = 0;
+    buf[i++] = report->type;
+    buf[i++] = report->code;
+    buf[i++] = 0;/* checksum */
+    buf[i++] = 0;/* skip checksum for now */
+    buf[i++] = 0;/* reserved */
+    buf[i++] = 0;/* reserved */
+    buf[i++] = report->num_records >> 8; /* MSB first */
+    buf[i++] = report->num_records & 0xFF;
+    /* Address record */
+    buf[i++] = report->records[0].record_type;
+    buf[i++] = report->records[0].aux_data_len;
+    buf[i++] = report->records[0].num_srcs >> 8;
+    buf[i++] = report->records[0].num_srcs & 0xFF;
+    memcpy(&buf[i],&report->records[0].mcast_address,16);
+    i += 16;
+    memcpy(&buf[i],&report->records[0].sources[0],16);
+    i += 16;
+
+    /* get checksum over buffer and write it back into the right place */
+    tmp = htons(ipv6_checksum((unsigned short*)buf, buflen));
+    msg_Dbg( p_access , "Ipv6 Checksum is 0x%x",tmp);
+    buf[2] = tmp >> 8;
+    buf[3] = tmp & 0xFF;
+
+    return i;
 }
 
 /*
@@ -999,10 +1401,13 @@ static void amt_send_relay_request( stream_t *p_access, char *relay_ip )
 * | Msg Type(1 byte)| Reserved (1 byte)| MAC (6 byte)| nonce (4 byte) | IGMP packet  |
 * +----------------------------------------------------------------------------------+
 */
-static void amt_send_mem_update( stream_t *p_access, char *relay_ip, bool leave)
+static int amt_send_mem_update( stream_t *p_access, bool leave)
 {
-    int           sendBufSize = IP_HDR_IGMP_LEN + MAC_LEN + NONCE_LEN + AMT_HDR_LEN;
-    char          pSendBuffer[ sendBufSize + IGMP_REPORT_LEN ];
+    int           i_amt_hdr_len = MAC_LEN + NONCE_LEN + AMT_HDR_LEN;
+    int           i_ipv6_hdr_len = IPv6_HOP_BY_HOP_OPTION_LEN + IPv6_FIXED_HDR_LEN;
+    int           i_sendBufSize = i_amt_hdr_len + IP_HDR_IGMP_LEN;
+    int           i_sendBufSizeIPv6 = i_amt_hdr_len + i_ipv6_hdr_len + MLD_REPORT_LEN;
+    uint8_t       pSendBuffer[ i_sendBufSize > i_sendBufSizeIPv6 ? i_sendBufSize : i_sendBufSizeIPv6 ]; /* make the buffer as large as needed */
     uint32_t      ulNonce = 0;
     access_sys_t *sys = p_access->p_sys;
 
@@ -1017,53 +1422,103 @@ static void amt_send_mem_update( stream_t *p_access, char *relay_ip, bool leave)
     ulNonce = sys->glob_ulNonce;
     memcpy( &pSendBuffer[8], &ulNonce, NONCE_LEN );
 
-    /* make IP header for IGMP packet */
-    amt_ip_alert_t p_ipHead;
-    memset( &p_ipHead, 0, IP_HDR_IGMP_LEN );
-    make_ip_header( &p_ipHead );
-
-    struct sockaddr_in temp;
-    int res = inet_pton( AF_INET, MCAST_ALLHOSTS, &(temp.sin_addr) );
-    if( res != 1 )
+    if ( sys->mcastGroupAddr.ipv4.sin_family == AF_INET )
     {
-        msg_Err(p_access, "Could not convert all hosts multicast address: %s", gai_strerror(errno) );
-        return;
-    }
-    p_ipHead.destAddr = temp.sin_addr.s_addr;
-    p_ipHead.check = get_checksum( (unsigned short*)&p_ipHead, IP_HDR_IGMP_LEN );
+        /* make IP header for IGMP packet */
+        amt_ip_alert_t p_ipHead;
+        memset( &p_ipHead, 0, IP_HDR_IGMP_LEN );
+        make_ip_header( &p_ipHead );
+
+        struct sockaddr_in temp;
+        int i_res = inet_pton( AF_INET, MCAST_ALLHOSTS, &(temp.sin_addr) );
+        if( i_res != 1 )
+        {
+            msg_Err(p_access, "Could not convert all hosts multicast address: %s", gai_strerror(errno) );
+            return -1;
+        }
+        p_ipHead.destAddr = temp.sin_addr.s_addr;
+        p_ipHead.check = get_checksum( (unsigned short*)&p_ipHead, IP_HDR_IGMP_LEN );
 
-    amt_igmpv3_groupRecord_t groupRcd;
-    groupRcd.auxDatalen = 0;
-    groupRcd.ssm = sys->mcastGroupAddr.sin_addr.s_addr;
+        amt_igmpv3_groupRecord_t groupRcd;
+        groupRcd.auxDatalen = 0;
+        groupRcd.ssm = sys->mcastGroupAddr.ipv4.sin_addr.s_addr;
 
-    if( sys->mcastSrcAddr.sin_addr.s_addr )
-    {
-        groupRcd.type = leave ? AMT_IGMP_BLOCK:AMT_IGMP_INCLUDE;
-        groupRcd.nSrc = htons(1);
-        groupRcd.srcIP[0] = sys->mcastSrcAddr.sin_addr.s_addr;
+        if( sys->mcastSrcAddr.ipv4.sin_addr.s_addr )
+        {
+            groupRcd.type = leave ? AMT_IGMP_BLOCK:AMT_IGMP_INCLUDE;
+            groupRcd.nSrc = htons(1);
+            groupRcd.srcIP[0] = sys->mcastSrcAddr.ipv4.sin_addr.s_addr;
 
-    } else {
-        groupRcd.type = leave ? AMT_IGMP_INCLUDE_CHANGE:AMT_IGMP_EXCLUDE_CHANGE;
-        groupRcd.nSrc = htons(0);
+        }
+        else
+        {
+            groupRcd.type = leave ? AMT_IGMP_INCLUDE_CHANGE:AMT_IGMP_EXCLUDE_CHANGE;
+            groupRcd.nSrc = htons(0);
+        }
+
+        /* make IGMP membership report */
+        amt_igmpv3_membership_report_t p_igmpMemRep;
+        make_report( &p_igmpMemRep );
+
+        memcpy(&p_igmpMemRep.grp[0], &groupRcd, (int)sizeof(groupRcd) );
+        p_igmpMemRep.checksum = get_checksum( (unsigned short*)&p_igmpMemRep, IGMP_REPORT_LEN );
+
+        amt_membership_update_msg_t memUpdateMsg;
+        memset(&memUpdateMsg, 0, sizeof(memUpdateMsg));
+        memcpy(&memUpdateMsg.ipHead, &p_ipHead, sizeof(p_ipHead) );
+        memcpy(&memUpdateMsg.encapsulated.igmp_memReport, &p_igmpMemRep, sizeof(p_igmpMemRep) );
+
+        memcpy( &pSendBuffer[12], &memUpdateMsg, sizeof(memUpdateMsg.ipHead) + sizeof(memUpdateMsg.encapsulated.igmp_memReport) );
+        send( sys->sAMT, pSendBuffer, i_sendBufSize + IGMP_REPORT_LEN , 0 );
     }
+    else
+    {
+
+        amt_ipv6_t ip;
+        memset( &ip, 0, sizeof(ip) );
+        amt_mldv2_listener_report_t report;
+        memset( &report, 0, sizeof(report) );
+
+        struct in6_addr tmp;
+        if( inet_pton( AF_INET6, MCAST_ALL_MLDv2_CAP_ROUTERS, &tmp ) != 1 )
+        {
+            msg_Err(p_access, "Could not convert all hosts multicast address: %s", gai_strerror(errno) );
+            return -1;
+        }
+
+        make_ipv6(&ip, IPv6_HOP_BY_HOP_OPTION_LEN + MLD_REPORT_LEN, &tmp);
+
+        if( make_mld_report(p_access,leave,&report,&sys->mcastGroupAddr.ipv6.sin6_addr,&sys->mcastSrcAddr.ipv6.sin6_addr) )
+        {
+            goto oom;
+        }
 
-    /* make IGMP membership report */
-    amt_igmpv3_membership_report_t p_igmpMemRep;
-    make_report( &p_igmpMemRep );
+        int i = serialize_ipv6_pkt(&ip,&pSendBuffer[i_amt_hdr_len],i_sendBufSizeIPv6 - MLD_REPORT_LEN - i_amt_hdr_len);
+        if ( !i )
+        {
+            msg_Err( p_access, "Couldnt serialize ipv6 packet");
+            goto fail;
+        }
+
+        if ( !serialize_mld_report(&report,&pSendBuffer[i_amt_hdr_len + i],MLD_REPORT_LEN,p_access) )
+        {
+            msg_Err( p_access, "Couldnt serialize mld report");
+            goto fail;
+        }
 
-    memcpy(&p_igmpMemRep.grp[0], &groupRcd, (int)sizeof(groupRcd) );
-    p_igmpMemRep.checksum = get_checksum( (unsigned short*)&p_igmpMemRep, IGMP_REPORT_LEN );
+        send( sys->sAMT, pSendBuffer, i_sendBufSizeIPv6, 0 );
 
-    amt_membership_update_msg_t memUpdateMsg;
-    memset(&memUpdateMsg, 0, sizeof(memUpdateMsg));
-    memcpy(&memUpdateMsg.ipHead, &p_ipHead, sizeof(p_ipHead) );
-    memcpy(&memUpdateMsg.memReport, &p_igmpMemRep, sizeof(p_igmpMemRep) );
+        vlc_obj_free( p_access, report.records[0].sources); /* allocd in the make_mld_report() function */
+    }
 
-    memcpy( &pSendBuffer[12], &memUpdateMsg, sizeof(memUpdateMsg) );
 
-    send( sys->sAMT, pSendBuffer, sizeof(pSendBuffer), 0 );
+    msg_Dbg( p_access, "AMT relay membership report sent");
+    return 0;
 
-    msg_Dbg( p_access, "AMT relay membership report sent to %s", relay_ip );
+    oom:
+    msg_Err( p_access, "Out of Memory!");
+    fail:
+    return -1;
 }
 
 /**
@@ -1101,9 +1556,7 @@ static bool amt_rcv_relay_adv( stream_t *p_access )
             return false;
     }
 
-    struct sockaddr temp;
-    socklen_t temp_size = sizeof( struct sockaddr );
-    ssize_t len = recvfrom( sys->sAMT, pkt, RELAY_ADV_MSG_LEN, 0, &temp, &temp_size );
+    ssize_t len = recvfrom( sys->sAMT, pkt, RELAY_ADV_MSG_LEN, 0, 0, 0 );
 
     if (len < 0)
     {
@@ -1112,36 +1565,35 @@ static bool amt_rcv_relay_adv( stream_t *p_access )
     }
 
     /* AMT Relay Advertisement data (RFC7450) */
-    struct {
-        uint32_t ulRcvNonce;
-        uint32_t ipAddr;
-        uint8_t  type;
-    } relay_adv_msg;
+    uint32_t ulRcvNonce;
+    uint8_t  type;
 
-    memcpy( &relay_adv_msg.type, &pkt[0], MSG_TYPE_LEN );
-    if( relay_adv_msg.type != AMT_RELAY_ADV )
+    memcpy( &type, &pkt[0], MSG_TYPE_LEN );
+    if( type != AMT_RELAY_ADV )
     {
         msg_Err( p_access, "Received message not an AMT relay advertisement, ignoring. ");
         return false;
     }
 
-    memcpy( &relay_adv_msg.ulRcvNonce, &pkt[NONCE_LEN], NONCE_LEN );
-    if( sys->glob_ulNonce != relay_adv_msg.ulRcvNonce )
+    memcpy( &ulRcvNonce, &pkt[NONCE_LEN], NONCE_LEN );
+    if( sys->glob_ulNonce != ulRcvNonce )
     {
-        msg_Err( p_access, "Discovery nonces differ! currNonce:%x rcvd%x", sys->glob_ulNonce, (uint32_t) ntohl(relay_adv_msg.ulRcvNonce) );
+        msg_Err( p_access, "Discovery nonces differ! currNonce: %x rcvd: %x", (uint32_t) htonl(sys->glob_ulNonce), (uint32_t) htonl(ulRcvNonce) );
         return false;
     }
 
-    memcpy( &relay_adv_msg.ipAddr, &pkt[8], 4 );
+    struct sockaddr_in relayAddr4;
+    relayAddr4.sin_port = htons( AMT_PORT );
+    relayAddr4.sin_family = AF_INET;
 
-    struct sockaddr_in relayAddr =
-    {
-        .sin_family       = AF_INET,
-        .sin_addr.s_addr  = relay_adv_msg.ipAddr,
-        .sin_port         = htons( AMT_PORT ),
-    };
+    struct sockaddr_in6 relayAddr6;
+    relayAddr6.sin6_port = htons( AMT_PORT );
+    relayAddr6.sin6_family = AF_INET6;
 
-    int nRet = connect( sys->sAMT, (struct sockaddr *)&relayAddr, sizeof(relayAddr) );
+    void *bin_address_start = (sys->relayDiscoAddr.ipv4.sin_family == AF_INET) ? (void*) &relayAddr4.sin_addr : (void*) &relayAddr6.sin6_addr;
+    memcpy( bin_address_start , &pkt[8], sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? 4 : 16);
+
+    int nRet = connect( sys->sAMT, sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? (struct sockaddr*)&relayAddr4 : (struct sockaddr*)&relayAddr6, sys->relayDiscoAddr.ipv4.sin_family == AF_INET ? sizeof(relayAddr4) : sizeof(relayAddr6) );
     if( nRet < 0 )
     {
         msg_Err( p_access, "Error connecting AMT UDP socket: %s", vlc_strerror(errno) );
@@ -1179,8 +1631,9 @@ static bool amt_rcv_relay_adv( stream_t *p_access )
  */
 static bool amt_rcv_relay_mem_query( stream_t *p_access )
 {
-    char pkt[RELAY_QUERY_MSG_LEN];
-    memset( pkt, 0, RELAY_QUERY_MSG_LEN );
+    int i_buf_len = RELAY_QUERY_MSG_LEN + AMT_IPV6_MAX_NUM_SOURCES*16;
+    char pkt[i_buf_len];
+    memset( pkt, 0, i_buf_len);
     struct pollfd ufd[1];
     access_sys_t *sys = p_access->p_sys;
 
@@ -1196,11 +1649,11 @@ static bool amt_rcv_relay_mem_query( stream_t *p_access )
             return false;
     }
 
-    ssize_t len = recv( sys->sAMT, pkt, RELAY_QUERY_MSG_LEN, 0 );
+    ssize_t len = recv( sys->sAMT, pkt, i_buf_len, 0 );
 
-    if (len < 0 || len != RELAY_QUERY_MSG_LEN)
+    if ( len <= 0 || (sys->mcastGroupAddr.ipv4.sin_family == AF_INET && len != (RELAY_QUERY_MSG_LEN - 40)) || (sys->mcastGroupAddr.ipv4.sin_family == AF_INET6 && len < RELAY_QUERY_MSG_LEN) ) /* subtract 40 for ipv4 case */
     {
-        msg_Err(p_access, "length less than zero");
+        msg_Err(p_access, "length of relay query message invalid!");
         return false;
     }
 
@@ -1210,97 +1663,87 @@ static bool amt_rcv_relay_mem_query( stream_t *p_access )
     memcpy( &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 );
+        msg_Warn( p_access, "Nonces are different rcvd: %x glob: %x", (uint32_t) htonl(sys->relay_mem_query_msg.ulRcvedNonce), (uint32_t) htonl(sys->glob_ulNonce) );
         return false;
     }
 
-    size_t shift = AMT_HDR_LEN + MAC_LEN + NONCE_LEN + IP_HDR_IGMP_LEN;
-    sys->relay_igmp_query.type = pkt[shift];
-    shift++; assert( shift < RELAY_QUERY_MSG_LEN);
-    sys->relay_igmp_query.max_resp_code = pkt[shift];
-    shift++; assert( shift < RELAY_QUERY_MSG_LEN);
-    memcpy( &sys->relay_igmp_query.checksum, &pkt[shift], 2 );
-    shift += 2; assert( shift < RELAY_QUERY_MSG_LEN);
-    memcpy( &sys->relay_igmp_query.ssmIP, &pkt[shift], 4 );
-    shift += 4; assert( shift < RELAY_QUERY_MSG_LEN);
-    sys->relay_igmp_query.s_qrv = pkt[shift];
-    shift++; assert( shift < RELAY_QUERY_MSG_LEN);
-    if( pkt[shift] == 0 )
-        sys->relay_igmp_query.qqic = 125;
+    if ( sys->mcastGroupAddr.ipv4.sin_family == AF_INET )
+    {
+        size_t shift = AMT_HDR_LEN + MAC_LEN + NONCE_LEN + IP_HDR_IGMP_LEN;
+        sys->relay_query.igmp.type = pkt[shift];
+        shift++; assert( shift < RELAY_QUERY_MSG_LEN);
+        sys->relay_query.igmp.max_resp_code = pkt[shift];
+        shift++; assert( shift < RELAY_QUERY_MSG_LEN);
+        memcpy( &sys->relay_query.igmp.checksum, &pkt[shift], 2 );
+        shift += 2; assert( shift < RELAY_QUERY_MSG_LEN);
+        memcpy( &sys->relay_query.igmp.ssmIP, &pkt[shift], 4 );
+        shift += 4; assert( shift < RELAY_QUERY_MSG_LEN);
+        sys->relay_query.igmp.s_qrv = pkt[shift];
+        shift++; assert( shift < RELAY_QUERY_MSG_LEN);
+        if( pkt[shift] == 0 )
+        {
+            sys->relay_query.igmp.qqic = 125;
+        }
+        else
+        {
+            sys->relay_query.igmp.qqic = pkt[shift];
+        }
+
+        shift++; assert( shift < RELAY_QUERY_MSG_LEN);
+        memcpy( &sys->relay_query.igmp.nSrc, &pkt[shift], 2 );
+    }
     else
-        sys->relay_igmp_query.qqic = pkt[shift];
+    {
+        uint16_t temp_short;
+        int offset = AMT_HDR_LEN + MAC_LEN + NONCE_LEN + IPv6_FIXED_HDR_LEN + IPv6_HOP_BY_HOP_OPTION_LEN;
+        sys->relay_query.mld.type = pkt[offset++];
+        sys->relay_query.mld.code = pkt[offset++];
+        memcpy( &temp_short, &pkt[offset], 2 );
+        sys->relay_query.mld.checksum = ntohs(temp_short);
+        offset += 2;
+        memcpy( &temp_short, &pkt[offset], 2 );
+        sys->relay_query.mld.max_resp_code = ntohs(temp_short);
+        offset += 4; /* += 2 for max resp code, += 2 more for reserved */
+        memcpy( &sys->relay_query.mld.mcast_address, &pkt[offset], 16); /* ipv6 addr */
+        offset += 16;
+        sys->relay_query.mld.qrv = pkt[offset] & (7); /* bottom 3 bits */
+        sys->relay_query.mld.s_flag = pkt[offset] & (1 << 3); /* 4th bit */
+        offset++;
+        sys->relay_query.mld.qqic = pkt[offset++];
+        if ( !sys->relay_query.mld.qqic )
+        {
+            sys->relay_query.mld.qqic = 125;
+        }
+        memcpy( &temp_short, &pkt[offset], 2 );
+        sys->relay_query.mld.num_srcs = ntohs(temp_short);
+        offset += 2;
+        if ( sys->relay_query.mld.num_srcs > AMT_IPV6_MAX_NUM_SOURCES )
+        {
+            msg_Err(p_access, "Too many source addresses in mld query - currently we only handle %d sources!",AMT_IPV6_MAX_NUM_SOURCES);
+            return false;
+        }
+        sys->relay_query.mld.srcs = vlc_obj_calloc(p_access, sys->relay_query.mld.num_srcs, 16);
+        if( unlikely( sys->relay_query.mld.srcs == NULL ) )
+        {
+            msg_Err(p_access, "AMT: Out of memory");
+            return VLC_ENOMEM;
+        }
+        for ( int i = 0; i < sys->relay_query.mld.num_srcs; i++ )
+        {
+            struct in6_addr tmp;
+            memcpy(&tmp,&pkt[offset],16);
+            sys->relay_query.mld.srcs[i] = tmp;
+            offset += 16;
+            char src_buf [INET6_ADDRSTRLEN];
+            msg_Dbg(p_access, "MLD Query- received multicast source address: %s",
+            inet_ntop(AF_INET6,&tmp,src_buf,INET6_ADDRSTRLEN));
+        }
 
-    shift++; assert( shift < RELAY_QUERY_MSG_LEN);
-    memcpy( &sys->relay_igmp_query.nSrc, &pkt[shift], 2 );
+    }
 
     return true;
 }
 
-/**
- * Join SSM group based on input addresses, or use the defaults
- * */
-static int amt_joinSSM_group( stream_t *p_access )
-{
-#ifdef IP_ADD_SOURCE_MEMBERSHIP
-    struct ip_mreq_source imr;
-    access_sys_t *sys = p_access->p_sys;
-
-    imr.imr_multiaddr.s_addr = sys->mcastGroupAddr.sin_addr.s_addr;
-    imr.imr_sourceaddr.s_addr = sys->mcastSrcAddr.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) );
-#else
-    errno = EINVAL;
-    return -1;
-#endif
-}
-
-static int amt_joinASM_group( stream_t *p_access )
-{
-    struct ip_mreq imr;
-    access_sys_t *sys = p_access->p_sys;
-
-    imr.imr_multiaddr.s_addr = sys->mcastGroupAddr.sin_addr.s_addr;
-    imr.imr_interface.s_addr = INADDR_ANY;
-
-    return setsockopt( sys->sAMT, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(imr) );
-}
-
-/**
- * Leave SSM group that was joined earlier.
- * */
-static int amt_leaveSSM_group( stream_t *p_access )
-{
-#ifdef IP_DROP_SOURCE_MEMBERSHIP
-    struct ip_mreq_source imr;
-    access_sys_t *sys = p_access->p_sys;
-
-    imr.imr_multiaddr.s_addr = sys->mcastGroupAddr.sin_addr.s_addr;
-    imr.imr_sourceaddr.s_addr = sys->mcastSrcAddr.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) );
-#else
-    errno = EINVAL;
-    return -1;
-#endif
-}
-
-/**
- * Leave ASM group that was joined earlier.
- * */
-static int amt_leaveASM_group( stream_t *p_access )
-{
-    struct ip_mreq imr;
-    access_sys_t *sys = p_access->p_sys;
-
-    imr.imr_multiaddr.s_addr = sys->mcastGroupAddr.sin_addr.s_addr;
-    imr.imr_interface.s_addr = INADDR_ANY;
-
-    return setsockopt( sys->sAMT, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&imr, sizeof(imr) );
-}
-
 /* A timer is spawned since IGMP membership updates need to issued periodically
  * in order to continue to receive multicast. */
 static void amt_update_timer_cb( void *data )
@@ -1308,10 +1751,10 @@ static void amt_update_timer_cb( void *data )
     stream_t     *p_access = (stream_t*) data;
     access_sys_t *sys = p_access->p_sys;
 
-    amt_send_mem_update( p_access, sys->relayDisco, false );
+    amt_send_mem_update( p_access, false );
 
     /* Arms the timer again for a single shot from this callback. That way, the
      * time spent in amt_send_mem_update() is taken into consideration. */
     vlc_timer_schedule( sys->updateTimer, false,
-                        VLC_TICK_FROM_SEC( sys->relay_igmp_query.qqic ), 0 );
+                        VLC_TICK_FROM_SEC( sys->mcastGroupAddr.ipv4.sin_family == AF_INET ? sys->relay_query.igmp.qqic : sys->relay_query.mld.qqic), 0 );
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/30b938df715c80031be1c5649c1740acc4a50b33

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/30b938df715c80031be1c5649c1740acc4a50b33
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list