2773 lines
92 KiB
C
2773 lines
92 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmcp_mld.c,v $ $Revision: 1.36.10.6 $
|
|
* $Source: /home/interpeak/CVSRoot/ipmcp/src/ipmcp_mld.c,v $
|
|
* $Author: svc-cmnet $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipmcp_config.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
#ifdef IPMCP_USE_MLD
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock2.h>
|
|
#include <ipcom_sock6.h>
|
|
#include <ipcom_sysctl.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sysvar.h>
|
|
|
|
#include <ipnet_routesock.h>
|
|
|
|
#include "ipmcp_h.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPMCP_MLD_IS_LINK_LOCAL_MCAST_ADDR(maddr) ((maddr)->in6.addr8[1] == 0x02)
|
|
|
|
/* Multicast Listener Discovery Definitions */
|
|
#define IPMCP_MLD_LISTENER_QUERY 130
|
|
#define IPMCP_MLD_LISTENER_REPORT 131
|
|
#define IPMCP_MLD_LISTENER_REDUCTION 132
|
|
#define IPMCP_MLD_LISTENER_REPORT_V2 143
|
|
|
|
#define IPMCP_MLD_MIN_PKT_LEN 24
|
|
|
|
#define IPMCP_ALIGNED_SIZE(size, align) ((align) * (((size) + (align) - 1) / (align)))
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* MLD header
|
|
*===========================================================================
|
|
*/
|
|
#include <ipcom_align16.h>
|
|
struct IP_PACKED1 Ipmcp_icmp6_hdr
|
|
{
|
|
Ip_u8 icmp6_type; /* type field */
|
|
Ip_u8 icmp6_code; /* code field */
|
|
Ip_u16 icmp6_cksum; /* checksum field */
|
|
union IP_PACKED1
|
|
{
|
|
Ip_u32 icmp6_un_data32[1]; /* type-specific field */
|
|
Ip_u16 icmp6_un_data16[2]; /* type-specific field */
|
|
Ip_u8 icmp6_un_data8[4]; /* type-specific field */
|
|
} IP_PACKED2 icmp6_dataun;
|
|
} IP_PACKED2;
|
|
|
|
#define ipmcp_mld_type icmp6.icmp6_type
|
|
#define ipmcp_mld_code icmp6.icmp6_code
|
|
#define ipmcp_mld_cksum icmp6.icmp6_cksum
|
|
#define ipmcp_mld_maxdelay icmp6.icmp6_dataun.icmp6_un_data16[0]
|
|
#define ipmcp_mld_reserved icmp6.icmp6_dataun.icmp6_un_data16[1]
|
|
|
|
|
|
typedef IP_PACKED1 struct Ipmcp_mld_hdr
|
|
{
|
|
struct Ipmcp_icmp6_hdr icmp6;
|
|
struct Ip_in6_addr group_address; /* multicast address */
|
|
}
|
|
IP_PACKED2 Ipmcp_mld_pkt_t;
|
|
|
|
|
|
typedef IP_PACKED1 struct Ipmcp_mld_query_pkt_struct
|
|
{
|
|
struct Ipmcp_icmp6_hdr icmp6;
|
|
struct Ip_in6_addr group_address;
|
|
Ip_u8 res_s_qrv; /* Reserved,
|
|
Suppress Router-Side Processing,
|
|
Querier's Robustness Variable */
|
|
Ip_u8 qqic; /* Querier's Query Interval Code */
|
|
Ip_u16 num_sources; /* Number of sources */
|
|
struct Ip_in6_addr source_addresses[1]; /* Multicast source addresses */
|
|
|
|
}
|
|
IP_PACKED2 Ipmcp_mld_query_pkt_t;
|
|
|
|
#define IPMCP_MLDV2_QUERY_SUPRESS_BIT 0x08
|
|
#define IPMCP_MLDV2_GET_QRV(query) ((query)->res_s_qrv & 0x07)
|
|
|
|
typedef IP_PACKED1 struct Ipmcp_mld_group_rec_pkt_struct
|
|
{
|
|
Ip_u8 type; /* One of the IPMCP_GROUP_REC_TYPE_xxx constants */
|
|
Ip_u8 aux_data_len; /* Auxilly data length in number of 32 bit words */
|
|
Ip_u16 num_sources; /* Number of sources */
|
|
struct Ip_in6_addr group_address; /* IPv4 multicast address */
|
|
struct Ip_in6_addr source_addresses[1]; /* Multicast source addresses */
|
|
}
|
|
IP_PACKED2 Ipmcp_mld_group_rec_pkt_t;
|
|
|
|
typedef IP_PACKED1 struct Ipmcp_mld_report_pkt_struct
|
|
{
|
|
Ip_u8 type; /* 143 */
|
|
Ip_u8 reserved;
|
|
Ip_u16 checksum; /* Internet checksum */
|
|
Ip_u16 reserved2;
|
|
Ip_u16 num_group_rec; /* Number of group records */
|
|
Ipmcp_mld_group_rec_pkt_t recs[1];
|
|
}
|
|
IP_PACKED2 Ipmcp_mld_report_pkt_t;
|
|
|
|
|
|
#include <ipcom_align16.h>
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipmcp_mld_rec_t
|
|
*===========================================================================
|
|
* MLDv2 record class
|
|
*/
|
|
typedef struct Ipmcp_mld_rec_struct
|
|
{
|
|
Ipmcp_rec_t base; /* Base class */
|
|
Ipmcp_mld_group_rec_pkt_t *rec; /* Pointer to an MLDv2 record */
|
|
}
|
|
Ipmcp_mld_rec_t;
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipmcp_mld_saddr_struct
|
|
*===========================================================================
|
|
* IPv6 multicast source address descriptor for a downstream interface.
|
|
*/
|
|
typedef struct Ipmcp_mld_saddr_struct
|
|
{
|
|
Ipmcp_saddr_t base; /* Base class */
|
|
struct Ip_in6_addr saddr; /* Source address */
|
|
} Ipmcp_mld_saddr_t;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC void ipmcp_mld_maddr_drop(Ipmcp_mld_maddr_t *self, void *unused);
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_set_sock_addr
|
|
*===========================================================================
|
|
* Description: Sets a sockets address from a inet6 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_set_sock_addr(struct Ip_sockaddr_storage *s, struct Ip_in6_addr *a)
|
|
{
|
|
struct Ip_sockaddr_in6 *i = (struct Ip_sockaddr_in6 *)s;
|
|
|
|
ipcom_memset(i, 0, sizeof(*i));
|
|
i->sin6_family = IP_AF_INET6;
|
|
IPCOM_SA_LEN_SET(i, sizeof(*i));
|
|
i->sin6_addr = *a;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_set_addr
|
|
*===========================================================================
|
|
* Description: Sets a sockets address from a inet6 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_set_addr(struct Ip_sockaddr_in6 *s, struct Ip_in6_addr *a)
|
|
{
|
|
ipmcp_mld_set_sock_addr((struct Ip_sockaddr_storage *) s, a);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_addr_key_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for an IPv6 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_mld_addr_key_hash(struct Ip_in6_addr *addr)
|
|
{
|
|
return ipcom_hash_update(addr, sizeof(*addr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_saddr_obj_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for the multicast source address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_mld_saddr_obj_hash(Ipmcp_mld_saddr_t *obj)
|
|
{
|
|
return ipcom_hash_update(&obj->saddr, sizeof(obj->saddr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_saddr_compare
|
|
*===========================================================================
|
|
* Description: Compares a MCP multicast source address entry with a IPv6 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_saddr_compare(Ipmcp_mld_saddr_t *obj , struct Ip_in6_addr *saddr)
|
|
{
|
|
return IP_IN6_ARE_ADDR_EQUAL(&obj->saddr, saddr);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_obj_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for the multicast address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_mld_maddr_obj_hash(Ipmcp_mld_maddr_t *obj)
|
|
{
|
|
return ipcom_hash_update(&obj->maddr, sizeof(obj->maddr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_compare
|
|
*===========================================================================
|
|
* Description: Compares two IPv6 addresses.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_maddr_compare(Ipmcp_mld_maddr_t *obj , struct Ip_in6_addr *maddr)
|
|
{
|
|
return IP_IN6_ARE_ADDR_EQUAL(&obj->maddr, maddr);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_obj_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for a upstream multicast address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_mld_upstream_maddr_obj_hash(Ipmcp_mld_upstream_maddr_t *obj)
|
|
{
|
|
return ipcom_hash_update(&obj->maddr, sizeof(obj->maddr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_compare
|
|
*===========================================================================
|
|
* Description: Compares an upstream multicast address entry with a IPv6 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_upstream_maddr_compare(Ipmcp_mld_upstream_maddr_t *obj , struct Ip_in6_addr *maddr)
|
|
{
|
|
return IP_IN6_ARE_ADDR_EQUAL(&obj->maddr, maddr);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_get_link_local_addr
|
|
*===========================================================================
|
|
* Description: Finds out the link local address of an specified interface
|
|
* Parameters: ifindex -
|
|
* link_local_addr - Buffer where the link local address will
|
|
* be stored.
|
|
* Returns: IPMCP_LINK_LOCAL_ADR_TENTATIVE = link local address exist but is tentative
|
|
* IPMCP_LINK_LOCAL_ADR_AVAIL = success, link local address exist
|
|
* IPMCP_LINK_LOCAL_ADR_NOTAVAIL = address could not be returned
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmcp_mld_get_link_local_addr(Ip_u32 ifindex, struct Ip_in6_addr *link_local_addr)
|
|
{
|
|
int name[6];
|
|
int ret = IPMCP_LINK_LOCAL_ADR_NOTAVAIL;
|
|
Ip_u8 *if_data;
|
|
struct Ipnet_ifa_msghdr *ifa;
|
|
struct Ipnet_if_msghdr *ifm;
|
|
struct Ip_sockaddr_in6 *addrs[IPNET_RTAX_MAX];
|
|
struct Ip_sockaddr_in6 *myaddr = IP_NULL;
|
|
Ip_size_t if_data_len;
|
|
Ip_size_t if_data_offset;
|
|
Ip_size_t offset;
|
|
|
|
/* Find out our link local address on this interface */
|
|
name[0] = IP_CTL_NET;
|
|
name[1] = IP_AF_ROUTE;
|
|
name[2] = 0;
|
|
name[3] = IP_AF_INET6;
|
|
name[4] = IP_NET_RT_IFLIST;
|
|
name[5] = ifindex;
|
|
|
|
if (ipcom_sysctl(name, 6, IP_NULL, &if_data_len, IP_NULL, 0) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_mld_get_link_local_addr: ipcom_sysctl() failed: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
return IPMCP_LINK_LOCAL_ADR_NOTAVAIL;
|
|
}
|
|
|
|
if_data = ipcom_malloc(if_data_len);
|
|
if (if_data == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipmcp_mld_get_link_local_addr: out of memory");
|
|
return IPMCP_LINK_LOCAL_ADR_NOTAVAIL;
|
|
}
|
|
|
|
if (ipcom_sysctl(name, 6, if_data, &if_data_len, IP_NULL, 0) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_mld_get_link_local_addr: ipcom_sysctl() failed: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
ret = IPMCP_LINK_LOCAL_ADR_NOTAVAIL;
|
|
goto cleanup;
|
|
}
|
|
|
|
ifm = (struct Ipnet_if_msghdr *) &if_data[0];
|
|
if_data_offset = ifm->ifm_msglen;
|
|
|
|
offset = 0;
|
|
while (offset < if_data_len - if_data_offset)
|
|
{
|
|
ifa = (struct Ipnet_ifa_msghdr *) &if_data[if_data_offset + offset];
|
|
ipmcp_route_init_addrs(ifa + 1, ifa->ifam_addrs, (struct Ip_sockaddr **) addrs);
|
|
|
|
offset += ifa->ifam_msglen;
|
|
|
|
if (IP_IN6_IS_ADDR_LINK_LOCAL(&addrs[IPNET_RTAX_IFA]->sin6_addr))
|
|
{
|
|
/* Found link local addr of this interface */
|
|
myaddr = addrs[IPNET_RTAX_IFA];
|
|
|
|
/* Verify that the link local address isn't still tentative */
|
|
struct Ip_in6_aliasreq ifareq;
|
|
|
|
ipcom_memset(&ifareq, 0, sizeof(ifareq));
|
|
if (ipcom_if_indextoname(ifa->ifam_index, ifareq.ifra_name) == IP_NULL)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_mld_get_link_local_addr: Could not get interface name for index %d",
|
|
ifa->ifam_index);
|
|
goto cleanup;
|
|
}
|
|
ipcom_memcpy(&ifareq.ifra_addr, addrs[IPNET_RTAX_IFA], sizeof(ifareq.ifra_addr));
|
|
(void)ipcom_socketioctl(ipmcp.ipd_fd, IP_SIOCXGIFADDR_IN6, &ifareq);
|
|
if (IP_BIT_ISSET(ifareq.ifra_flags, IP_IN6_IFF_TENTATIVE))
|
|
{
|
|
IPCOM_LOG1(DEBUG, "ipmcp_mld_get_link_local_addr: link-local address still tentative on interface %s",
|
|
ifareq.ifra_name);
|
|
ret = IPMCP_LINK_LOCAL_ADR_TENTATIVE;
|
|
}
|
|
else
|
|
{
|
|
ret = IPMCP_LINK_LOCAL_ADR_AVAIL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (myaddr == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(INFO, "MLD: no link-local address found on interface");
|
|
goto cleanup;
|
|
}
|
|
|
|
ipcom_memcpy(link_local_addr, &myaddr->sin6_addr, sizeof(struct Ip_in6_addr));
|
|
|
|
cleanup:
|
|
ipcom_free(if_data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_get_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_mld_upstream_maddr_get_addr(Ipmcp_mld_upstream_maddr_t *maddr)
|
|
{
|
|
return &maddr->maddr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_change_mode
|
|
*===========================================================================
|
|
* Description: Changes filter mode of the upstream address.
|
|
* Parameters: maddr - A upstream multicast address.
|
|
* to_exclude - IP_TRUE if the mode should be changed to exclude
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_upstream_maddr_change_mode(Ipmcp_mld_upstream_maddr_t *maddr, Ip_bool to_exclude)
|
|
{
|
|
struct Ip_group_req req;
|
|
|
|
/* Leave this group if it has been joined */
|
|
req.gr_interface = ipmcp.upstream_if.ifindex;
|
|
ipmcp_mld_set_sock_addr(&req.gr_group, &maddr->maddr);
|
|
(void) ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MCAST_LEAVE_GROUP,
|
|
&req, sizeof(req));
|
|
|
|
ipcom_set_remove_all(maddr->base.filter);
|
|
maddr->base.filter->user = (to_exclude ? IPCOM_SET_IS_EXCLUDE : IPCOM_SET_IS_INCLUDE);
|
|
|
|
if (to_exclude)
|
|
{
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MCAST_JOIN_GROUP,
|
|
&req, sizeof(req)) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipmcp_mld_upstream_maddr_change_mode: setsockopt IPPROTO_IP, MCAST_JOIN_GROUP failed on %s: %s",
|
|
ipmcp.upstream_if.ifname, ipcom_strerror(ipcom_errno));
|
|
IP_PANIC2();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_add_source
|
|
*===========================================================================
|
|
* Description: Adds a new source address to the current filter.
|
|
* Parameters: maddr - A upstream multicast address.
|
|
* src - The source address to add to the filter.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_upstream_maddr_add_source(Ipmcp_mld_upstream_maddr_t *maddr, struct Ip_in6_addr *src)
|
|
{
|
|
struct Ip_group_source_req sreq;
|
|
|
|
if (ipcom_set_add(maddr->base.filter, src) != IPCOM_SUCCESS)
|
|
{
|
|
IP_PANIC2();
|
|
return;
|
|
}
|
|
|
|
sreq.gsr_interface = ipmcp.upstream_if.ifindex;
|
|
ipmcp_mld_set_sock_addr(&sreq.gsr_group, &maddr->maddr);
|
|
ipmcp_mld_set_sock_addr(&sreq.gsr_source, src);
|
|
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
(maddr->base.filter->user == IPCOM_SET_IS_EXCLUDE ? IP_MCAST_BLOCK_SOURCE : IP_MCAST_JOIN_SOURCE_GROUP),
|
|
&sreq, sizeof(sreq)) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipmcp_mld_upstream_maddr_add_source: setsockopt failed on %s: %s",
|
|
ipmcp.upstream_if.ifname, ipcom_strerror(ipcom_errno));
|
|
IP_PANIC2();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_del_source
|
|
*===========================================================================
|
|
* Description: Deletes a source address to the current filter.
|
|
* Parameters: maddr - A upstream multicast address.
|
|
* src - The source address to remove from the filter.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_upstream_maddr_del_source(Ipmcp_mld_upstream_maddr_t *maddr, struct Ip_in6_addr *src)
|
|
{
|
|
struct Ip_group_source_req sreq;
|
|
|
|
if (ipcom_set_remove(maddr->base.filter, src) != IPCOM_SUCCESS)
|
|
{
|
|
IP_PANIC2();
|
|
return;
|
|
}
|
|
|
|
sreq.gsr_interface = ipmcp.upstream_if.ifindex;
|
|
ipmcp_mld_set_sock_addr(&sreq.gsr_group, &maddr->maddr);
|
|
ipmcp_mld_set_sock_addr(&sreq.gsr_source, src);
|
|
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
(maddr->base.filter->user == IPCOM_SET_IS_EXCLUDE
|
|
? IP_MCAST_UNBLOCK_SOURCE
|
|
: IP_MCAST_LEAVE_SOURCE_GROUP),
|
|
&sreq, sizeof(sreq)) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipmcp_mld_upstream_maddr_del_source: setsockopt failed on %s: %s",
|
|
ipmcp.upstream_if.ifname, ipcom_strerror(ipcom_errno));
|
|
IP_PANIC2();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_flush_mroutes
|
|
*===========================================================================
|
|
* Description: Removes all multicast forwarding entries for the added for
|
|
* the specified multicast group.
|
|
* Parameters: maddr_obj - A multicast group.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_upstream_maddr_flush_mroutes(Ipmcp_mld_upstream_maddr_t *maddr)
|
|
{
|
|
int i;
|
|
struct Ip_mf6cctl mf6ctl;
|
|
|
|
ipcom_memset(&mf6ctl, 0, sizeof(mf6ctl));
|
|
ipmcp_mld_set_addr(&mf6ctl.mf6cc_mcastgrp, &maddr->maddr);
|
|
mf6ctl.mf6cc_parent = ipmcp.upstream_if.mld.mif_index;
|
|
|
|
for (i = 0; i < IPMCP_MLD_MAX_SRC_ADDRS; i++)
|
|
{
|
|
if (IP_IN6_IS_ADDR_UNSPECIFIED(&maddr->saddrs[i]))
|
|
continue;
|
|
|
|
ipmcp_mld_set_addr(&mf6ctl.mf6cc_origin, &maddr->saddrs[i]);
|
|
|
|
IPCOM_LOG2(DEBUG, "MLD: deleting mcast route from %s to group %s",
|
|
ipcom_inet_ntop(IP_AF_INET6, &mf6ctl.mf6cc_origin, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET6, &mf6ctl.mf6cc_mcastgrp, ipmcp.log_buf + IP_INET6_ADDRSTRLEN, sizeof(ipmcp.log_buf)));
|
|
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MRT6_DEL_MFC,
|
|
&mf6ctl,
|
|
sizeof(mf6ctl)) < 0)
|
|
{
|
|
IPCOM_LOG4(ERR, "ipmcp_mld_upstream_maddr_flush_mroutes: "
|
|
"setsockopt IPPROTO_IPV6, MRT6_DEL_MFC failed on %s [%s %s]: %s",
|
|
ipmcp.upstream_if.ifname,
|
|
ipcom_inet_ntop(IP_AF_INET6, &mf6ctl.mf6cc_mcastgrp,
|
|
ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET6, &mf6ctl.mf6cc_mcastgrp,
|
|
ipmcp.log_buf + IP_INET6_ADDRSTRLEN, sizeof(ipmcp.log_buf)),
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
maddr->saddrs[i] = ip_in6addr_any;
|
|
}
|
|
maddr->next_src_addr = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_flush_all_mroutes_cb
|
|
*===========================================================================
|
|
* Description: Adapter function for ipmcp_mld_upstream_maddr_flush_mroutes()
|
|
* Parameters: upstream_maddr - An upstream multicast address.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_flush_all_mroutes_cb(Ipmcp_mld_upstream_maddr_t *upstream_maddr,
|
|
void *unused)
|
|
{
|
|
(void) unused;
|
|
ipmcp_mld_upstream_maddr_flush_mroutes(upstream_maddr);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_flush_all_mroutes
|
|
*===========================================================================
|
|
* Description: Flushes all IPv6 multicast routes.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_flush_all_mroutes(void)
|
|
{
|
|
ipcom_hash_for_each(ipmcp.upstream_if.mld.maddr_head,
|
|
(Ipcom_hash_foreach_cb_func)ipmcp_mld_flush_all_mroutes_cb,
|
|
IP_NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_get
|
|
*===========================================================================
|
|
* Description: Returns the upstream multicast address object, it is created
|
|
* if needed
|
|
* Parameters: maddr - An IPv4 multicast address.
|
|
* Returns: Upstream multicast object.
|
|
*
|
|
*/
|
|
IP_STATIC Ipmcp_mld_upstream_maddr_t *
|
|
ipmcp_mld_upstream_maddr_get(struct Ip_in6_addr *maddr)
|
|
{
|
|
Ipmcp_mld_upstream_maddr_t *maddr_obj;
|
|
|
|
maddr_obj = ipcom_hash_get(ipmcp.upstream_if.mld.maddr_head, maddr);
|
|
if (maddr_obj == IP_NULL)
|
|
{
|
|
maddr_obj = ipcom_calloc(1, sizeof(Ipmcp_mld_upstream_maddr_t));
|
|
if (maddr_obj == IP_NULL)
|
|
goto out_of_memory;
|
|
|
|
maddr_obj->base.filter = ipcom_set_new(sizeof(struct Ip_in6_addr));
|
|
if (maddr_obj->base.filter == IP_NULL)
|
|
goto out_of_memory;
|
|
|
|
maddr_obj->maddr = *maddr;
|
|
maddr_obj->base.ops = &ipmcp.mld.upstream_maddr_ops;
|
|
|
|
if (ipcom_hash_add(ipmcp.upstream_if.mld.maddr_head, maddr_obj) != IPCOM_SUCCESS)
|
|
goto out_of_memory;
|
|
}
|
|
|
|
return maddr_obj;
|
|
|
|
out_of_memory:
|
|
if (maddr_obj)
|
|
{
|
|
ipcom_set_delete(maddr_obj->base.filter);
|
|
ipcom_free(maddr_obj);
|
|
}
|
|
IPCOM_LOG0(ERR, "ipmcp_mld_upstream_maddr_get: Failed to allocate memory");
|
|
IP_PANIC2();
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_delete
|
|
*===========================================================================
|
|
* Description: Deletes a multicast address from the upstream interface.
|
|
* Parameters: maddr - The address to delete from the upstream interface.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_upstream_maddr_delete(Ipmcp_mld_upstream_maddr_t *maddr)
|
|
{
|
|
struct Ip_group_req req;
|
|
|
|
if (ipcom_hash_remove(ipmcp.upstream_if.mld.maddr_head, maddr) != IPCOM_SUCCESS)
|
|
IP_PANIC();
|
|
|
|
req.gr_interface = ipmcp.upstream_if.ifindex;
|
|
ipmcp_mld_set_sock_addr(&req.gr_group, &maddr->maddr);
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MCAST_LEAVE_GROUP,
|
|
&req, sizeof(req)) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipmcp_mld_upstream_maddr_delete: "
|
|
"setsockopt IPPROTO_IP, IP_MCAST_LEAVE_GROUP failed on %s: %s",
|
|
ipmcp.upstream_if.ifname, ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
ipmcp_mld_upstream_maddr_flush_mroutes(maddr);
|
|
ipcom_set_delete(maddr->base.filter);
|
|
ipcom_free(maddr);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_maddr_delete_cb
|
|
*===========================================================================
|
|
* Description: Deletes a multicast address from the upstream interface.
|
|
* Parameters: maddr_obj - The address to destroy.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_upstream_maddr_delete_cb(Ipmcp_mld_upstream_maddr_t *maddr_obj, void *unused)
|
|
{
|
|
(void) unused;
|
|
ipmcp_mld_upstream_maddr_delete(maddr_obj);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_store_mroute
|
|
*===========================================================================
|
|
* Description: Stores that a multicast route has been added from the
|
|
* specified source to the specified multicast address.
|
|
* Parameters: maddr - A multicast address that traffic has been sent to..
|
|
* src - The source of the multicast traffic
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_store_mroute(struct Ip_in6_addr *maddr, struct Ip_in6_addr *src)
|
|
{
|
|
Ipmcp_mld_upstream_maddr_t *maddr_obj;
|
|
|
|
maddr_obj = ipmcp_mld_upstream_maddr_get(maddr);
|
|
if (maddr_obj == IP_NULL)
|
|
/* No matching multicast address in the database */
|
|
return IP_FALSE;
|
|
|
|
if (IP_FALSE == IP_IN6_IS_ADDR_UNSPECIFIED(&maddr_obj->saddrs[maddr_obj->next_src_addr]))
|
|
{
|
|
/* Ring buffer overflow, remove the oldest entry */
|
|
struct Ip_mf6cctl mf6ctl;
|
|
|
|
IPCOM_LOG2(DEBUG, "MLD: deleting mcast route from %s to group %s",
|
|
ipcom_inet_ntop(IP_AF_INET6, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET6, maddr, ipmcp.log_buf + IP_INET6_ADDRSTRLEN, sizeof(ipmcp.log_buf)));
|
|
|
|
ipcom_memset(&mf6ctl, 0, sizeof(mf6ctl));
|
|
ipmcp_mld_set_addr(&mf6ctl.mf6cc_origin, &maddr_obj->saddrs[maddr_obj->next_src_addr]);
|
|
ipmcp_mld_set_addr(&mf6ctl.mf6cc_mcastgrp, &maddr_obj->maddr);
|
|
mf6ctl.mf6cc_parent = ipmcp.upstream_if.mld.mif_index;
|
|
(void)ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MRT6_DEL_MFC,
|
|
&mf6ctl,
|
|
sizeof(mf6ctl));
|
|
}
|
|
|
|
maddr_obj->saddrs[maddr_obj->next_src_addr] = *src;
|
|
maddr_obj->next_src_addr = (maddr_obj->next_src_addr + 1) % IPMCP_MLD_MAX_SRC_ADDRS;
|
|
return IP_TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_netif_to_maddr_head
|
|
*===========================================================================
|
|
* Description: Returns the IPv4 multicast address object head.
|
|
* Parameters: netif - A downstream interface.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipcom_hash *
|
|
ipmcp_mld_netif_to_maddr_head(Ipmcp_netif_t *netif)
|
|
{
|
|
return netif->mld.maddr_head;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_update_upstream_if_filter
|
|
*===========================================================================
|
|
* Description: Recalculates the filter mode for the upstream interface
|
|
* for the specified multicast address
|
|
* Parameters: maddr - The multicast address this route will target.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_update_upstream_if_filter(Ipmcp_mld_maddr_t *maddr)
|
|
{
|
|
Ipmcp_mld_upstream_maddr_t *maddr_obj;
|
|
|
|
maddr_obj = ipmcp_mld_upstream_maddr_get(&maddr->maddr);
|
|
if (maddr_obj != IP_NULL)
|
|
{
|
|
ipmcp_mld_upstream_maddr_flush_mroutes(maddr_obj);
|
|
ipmcp_update_upstream_if_filter(&maddr_obj->base, ipmcp_mld_netif_to_maddr_head);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_forward_stream
|
|
*===========================================================================
|
|
* Description: Returns if the (maddr, saddr) stream should be forwared to
|
|
* the specified interface.
|
|
* Parameters: netif - The interface the stream should be forwared to.
|
|
* maddr - A multicast address
|
|
* saddr - A multicast source address.
|
|
* Returns: IP_TRUE if this stream should be forwared.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_forward_stream(Ipmcp_netif_t *netif,
|
|
struct Ip_in6_addr *maddr,
|
|
struct Ip_in6_addr *saddr)
|
|
{
|
|
Ipmcp_maddr_t *maddr_obj;
|
|
Ipmcp_saddr_t *saddr_obj;
|
|
|
|
if (netif->mld.state != IPMCP_LINK_STATE_QUERIER)
|
|
return IP_FALSE;
|
|
|
|
maddr_obj = ipcom_hash_get(netif->mld.maddr_head, maddr);
|
|
if (maddr_obj == IP_NULL)
|
|
return IP_FALSE;
|
|
|
|
saddr_obj = maddr_obj->ops->get_saddr(maddr_obj, saddr, maddr_obj->mode_is_include);
|
|
if (maddr_obj->mode_is_include)
|
|
return saddr_obj != IP_NULL;
|
|
return saddr_obj == IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_add_mroute
|
|
*===========================================================================
|
|
* Description: Adds a multicast route entry.
|
|
* Parameters: mifi - The multicast interface the packet entered at.
|
|
* maddr - The multicast address this route will target.
|
|
* src - The source of the multicast packets.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmcp_mld_add_mroute(Ip_mifi_t mifi, struct Ip_in6_addr *maddr, struct Ip_in6_addr *src)
|
|
{
|
|
int n_mif = 0;
|
|
struct Ip_mf6cctl mfctl;
|
|
Ipmcp_netif_t *netif;
|
|
Ip_bool add_mroute = IP_FALSE;
|
|
|
|
/* Setup multicast route */
|
|
ipcom_memset(&mfctl, 0, sizeof(mfctl));
|
|
mfctl.mf6cc_parent = mifi;
|
|
ipmcp_mld_set_addr(&mfctl.mf6cc_mcastgrp, maddr);
|
|
ipmcp_mld_set_addr(&mfctl.mf6cc_origin, src);
|
|
|
|
if (mifi == ipmcp.upstream_if.mld.mif_index)
|
|
add_mroute = IP_TRUE;
|
|
else
|
|
{
|
|
/* Must always forward to the upstream interface when
|
|
multicast traffic entrers at one of the downstream interfaces */
|
|
n_mif++;
|
|
IP_MRT6_IF_SET(ipmcp.upstream_if.mld.mif_index, &mfctl.mf6cc_ifset);
|
|
}
|
|
|
|
/* Check MRT table on each interface */
|
|
for (netif = IPCOM_LIST_FIRST(&ipmcp.downstream_ifs_head);
|
|
netif;
|
|
netif = IPCOM_LIST_NEXT(&netif->list))
|
|
{
|
|
if (mifi == netif->mld.mif_index)
|
|
add_mroute = (netif->mld.state == IPMCP_LINK_STATE_QUERIER);
|
|
else if (ipmcp_mld_forward_stream(netif, maddr, src))
|
|
{
|
|
/* Found an interface to forward on */
|
|
n_mif++;
|
|
IP_MRT6_IF_SET(netif->mld.mif_index, &mfctl.mf6cc_ifset);
|
|
}
|
|
}
|
|
|
|
if (!add_mroute)
|
|
/* Not received on one of the downstream interfaces where the
|
|
proxy is querier or at the upstream interface */
|
|
return 0;
|
|
|
|
IPCOM_LOG2(DEBUG, "MLD: adding mcast route from %s to group %s",
|
|
ipcom_inet_ntop(IP_AF_INET6, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET6, maddr, ipmcp.log_buf + IP_INET6_ADDRSTRLEN, sizeof(ipmcp.log_buf)));
|
|
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MRT6_ADD_MFC,
|
|
&mfctl, sizeof(mfctl)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipmcp_mld_add_mroute: failed to add mroute: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
return -ipcom_errno;
|
|
}
|
|
|
|
ipmcp_mld_store_mroute(maddr, src);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_encode_duration
|
|
*===========================================================================
|
|
* Description: Encodes query interface to the queriers query interval code.
|
|
* Parameters: query_interval - Duration in seconds.
|
|
* Returns: Maximum response code in network byte order.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u16
|
|
ipmcp_mld_encode_duration(Ip_u32 duration, int version)
|
|
{
|
|
int expo;
|
|
Ip_u16 code;
|
|
|
|
if (version == 1 || duration < 32768)
|
|
return ip_htons((Ip_u16) duration);
|
|
|
|
expo = 0;
|
|
while ((duration >> (expo + 3)) > 0x1fff)
|
|
expo++;
|
|
|
|
code = 0x8000 + (expo << 12) + (0x0fff & (duration >> (expo + 3)));
|
|
return ip_htons(code);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_add_saddr_to_query
|
|
*===========================================================================
|
|
* Description: Adds a source address to a query.
|
|
* Parameters: saddr - A source address to add.
|
|
* query - The query message buffer.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_add_saddr_to_query(struct Ip_in6_addr *saddr, Ipmcp_mld_query_pkt_t *query)
|
|
{
|
|
query->source_addresses[query->num_sources++] = *saddr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_query_send
|
|
*===========================================================================
|
|
* Description: Send MLD query (general or specific).
|
|
* Parameters: netif - The interface to send the query at.
|
|
* maddr - The multicast address to send the query for.
|
|
* max_response_interval_msec - The maximum response time.
|
|
* suppress_router_side_processing - IP_TRUE if the S bit
|
|
* should be set.
|
|
* saddrs - The set of sources to include in the query,
|
|
* IP_NULL is equal to empty set.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_query_send(Ipmcp_netif_t *netif,
|
|
struct Ip_in6_addr *maddr,
|
|
Ip_u32 max_response_interval_msec,
|
|
Ip_bool suppress_router_side_processing,
|
|
Ipcom_set *saddrs)
|
|
{
|
|
Ipmcp_mld_query_pkt_t *pkt = (Ipmcp_mld_query_pkt_t *) ipmcp.send_buf;
|
|
struct Ip_sockaddr_in6 to;
|
|
struct Ip_msghdr m;
|
|
struct Ip_iovec iov;
|
|
struct Ip_in6_pktinfo *pktinfo;
|
|
struct Ip_cmsghdr *c;
|
|
Ip_u8 cmsg_buf[IP_CMSG_SPACE(sizeof(struct Ip_in6_pktinfo))];
|
|
int version;
|
|
unsigned msg_len;
|
|
|
|
version = ipcom_sysvar_get_as_int("ipmcp.mld.CompatibilityMode", 2);
|
|
|
|
ipcom_memset(&to, 0, sizeof(to));
|
|
IPCOM_SA_LEN_SET(&to, sizeof(to));
|
|
to.sin6_family = IP_AF_INET6;
|
|
to.sin6_addr = *maddr;
|
|
if (IP_IN6_IS_ADDR_MC_LINKLOCAL(maddr))
|
|
to.sin6_scope_id = netif->ifindex;
|
|
|
|
ipcom_memset(pkt, 0, sizeof(*pkt));
|
|
pkt->ipmcp_mld_type = IPMCP_MLD_LISTENER_QUERY;
|
|
pkt->ipmcp_mld_maxdelay =
|
|
ipmcp_mld_encode_duration(max_response_interval_msec, version);
|
|
if (IP_IN6_ARE_ADDR_EQUAL(&ipmcp.mld.all_hosts_addr, maddr) == IP_FALSE)
|
|
ipcom_memcpy(&pkt->group_address, maddr, sizeof(struct Ip_in6_addr));
|
|
|
|
if (version == 1)
|
|
msg_len = 24;
|
|
else
|
|
{
|
|
ip_assert(version == 2);
|
|
msg_len = 28;
|
|
if (ipmcp.config.robustness_variable <= 7)
|
|
/* RFC3810 - 5.1.8.
|
|
If non-zero, the QRV field contains the [Robustness Variable] value
|
|
used by the Querier. If the Querier's [Robustness Variable] exceeds
|
|
7 (the maximum value of the QRV field), the QRV field is set to zero. */
|
|
pkt->res_s_qrv = (Ip_u8) ipmcp.config.robustness_variable;
|
|
if (suppress_router_side_processing)
|
|
IP_BIT_SET(pkt->res_s_qrv, IPMCP_MLDV2_QUERY_SUPRESS_BIT);
|
|
pkt->qqic = ipmcp_qqic_from_msec(ipmcp.config.query_interval, 3);
|
|
|
|
}
|
|
|
|
if (saddrs != IP_NULL)
|
|
{
|
|
ipcom_set_for_each(saddrs,
|
|
(Ipcom_set_foreach_cb_func) ipmcp_mld_add_saddr_to_query,
|
|
pkt);
|
|
msg_len += pkt->num_sources * 16;
|
|
pkt->num_sources = ip_htons(pkt->num_sources);
|
|
}
|
|
|
|
iov.iov_base = pkt;
|
|
iov.iov_len = msg_len;
|
|
|
|
ipcom_memset(cmsg_buf, 0, sizeof(cmsg_buf));
|
|
c = (struct Ip_cmsghdr *) cmsg_buf;
|
|
c->cmsg_len = IP_CMSG_LEN(sizeof (struct Ip_in6_pktinfo));
|
|
c->cmsg_level = IP_IPPROTO_IPV6;
|
|
c->cmsg_type = IP_IPV6_PKTINFO;
|
|
|
|
pktinfo = IP_CMSG_DATA(c);
|
|
pktinfo->ipi6_ifindex = netif->ifindex;
|
|
/* RFC 3590. MLD queries MUST be sent with a valid link local address */
|
|
ipcom_memcpy(pktinfo->ipi6_addr.in6.addr8,
|
|
netif->mld.link_local_address.in6.addr8,
|
|
sizeof(pktinfo->ipi6_addr.in6));
|
|
|
|
m.msg_name = &to;
|
|
m.msg_namelen = sizeof(to);
|
|
m.msg_iov = &iov;
|
|
m.msg_iovlen = 1;
|
|
m.msg_control = c;
|
|
m.msg_controllen = sizeof(cmsg_buf);
|
|
m.msg_flags = 0;
|
|
|
|
IPCOM_LOG2(DEBUG2, "ipmcp_mld_query_send: Send query to %s on interface %s",
|
|
ipcom_inet_ntop(IP_AF_INET6, &to, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_if_indextoname(netif->ifindex, ipmcp.log_buf + IP_INET6_ADDRSTRLEN));
|
|
|
|
if (ipcom_sendmsg(ipmcp.mld.mld_fd, &m, 0) >= 0)
|
|
netif->mld.no_rt_count = 0;
|
|
else
|
|
{
|
|
/* This construct is just to suppress error message during duplicate address detection */
|
|
if (ipcom_errno != IP_ERRNO_EHOSTUNREACH || ++netif->mld.no_rt_count > 3)
|
|
{
|
|
IPCOM_LOG2(ERR, "MLD: Failed to send message on interface %s: %s",
|
|
ipcom_if_indextoname(netif->ifindex, ipmcp.log_buf), ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_general_query_tmo
|
|
*===========================================================================
|
|
* Description: Timeout routine used for repeatedly sending general queries
|
|
* on an interface.
|
|
* Parameters: netif - The interface the general query should be sent at.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_general_query_tmo(Ipmcp_netif_t *netif)
|
|
{
|
|
Ipmcp_mld_netif_t *i = &netif->mld;
|
|
Ip_bool restart_timer = IP_TRUE;
|
|
|
|
if (i->state == IPMCP_LINK_STATE_INACTIVE)
|
|
/* Retry later (this is probably because of no link layer address) */
|
|
return IP_TRUE;
|
|
|
|
ipmcp_mld_query_send(netif,
|
|
&ipmcp.mld.all_hosts_addr,
|
|
ipmcp.config.query_response_interval,
|
|
IP_FALSE,
|
|
IP_NULL);
|
|
|
|
if (i->startup_query_count > 0)
|
|
{
|
|
Ip_u32 duration;
|
|
|
|
if (--i->startup_query_count == 0)
|
|
/* Normal queries */
|
|
duration = ipmcp.config.query_interval;
|
|
else
|
|
/* Startup queries */
|
|
duration = ipmcp.config.startup_query_interval;
|
|
|
|
(void)ipcom_tmo2_request(ipmcp.tmo_head,
|
|
&i->timer,
|
|
(Ipcom_tmo2_func)ipmcp_mld_general_query_tmo,
|
|
netif,
|
|
duration);
|
|
restart_timer = IP_FALSE;
|
|
}
|
|
|
|
return restart_timer;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_resume_querier_tmo
|
|
*===========================================================================
|
|
* Description: Timeout routine called when resuming to querier state.
|
|
* Parameters: netif - The link to resume as querier.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_resume_querier_tmo(Ipmcp_netif_t *netif)
|
|
{
|
|
IPCOM_LOG1(INFO, "MLD: Link state QUERIER on %s", netif->ifname);
|
|
/* Must flush all routes since some or all of them might want to
|
|
include this interface as an outgoing interface now
|
|
RFC4605 chapter 3
|
|
...
|
|
When the proxy device receives a packet destined for a multicast
|
|
group (channel in Source-Specific Multicast (SSM)), it uses a list
|
|
consisting of the upstream interface and any downstream interface
|
|
that has a subscription pertaining to this packet and on which it is
|
|
the MLD Querier.
|
|
...
|
|
*/
|
|
ipmcp_mld_flush_all_mroutes();
|
|
netif->mld.state = IPMCP_LINK_STATE_QUERIER;
|
|
netif->mld.robustness_variable = ipmcp.config.robustness_variable;
|
|
netif->mld.query_interval = ipmcp.config.query_interval;
|
|
netif->mld.startup_query_count = ipmcp.config.startup_query_count;
|
|
ipmcp_mld_general_query_tmo(netif);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_version_1_compat_tmo
|
|
*===========================================================================
|
|
* Description: Called when MLDv1 compatibilty mode can be exited
|
|
* Parameters: maddr - A multicast address.
|
|
* Returns: IP_FALSE - The timeout should not be rescheduled.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_version_1_compat_tmo(Ipmcp_mld_maddr_t *maddr)
|
|
{
|
|
(void) maddr;
|
|
IPCOM_LOG1(INFO,
|
|
"MLD: Exit MLDv1 compatibility mode on %s",
|
|
maddr->base.netif->ifname);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_specific_query_tmo
|
|
*===========================================================================
|
|
* Description: Called when max response period passed since the last
|
|
* specific query on 'maddr'
|
|
* Parameters: maddr - A multicast address.
|
|
* Returns: IP_FALSE - The timeout should not be rescheduled.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_specific_query_tmo(Ipmcp_mld_maddr_t *maddr)
|
|
{
|
|
(void) maddr;
|
|
IPCOM_LOG1(DEBUG,
|
|
"MLD: Specific query finished on %s since max response period passed",
|
|
maddr->base.netif->ifname);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_other_querier_present_timeout
|
|
*===========================================================================
|
|
* Description: Returns the other querier present timeout in msec.
|
|
* Parameters: i - MLD specific interface config
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u32
|
|
ipmcp_mld_other_querier_present_timeout(Ipmcp_mld_netif_t *i)
|
|
{
|
|
/*
|
|
* RFC 3810
|
|
*
|
|
* The Other Querier Present Interval is the length of time that
|
|
* must pass before a multicast router decides that there is no
|
|
* longer another multicast router which should be the querier.
|
|
* This value MUST be ((the Robustness Variable) times (the Query
|
|
* Interval)) plus (one half of one Query Response Interval).
|
|
*/
|
|
return i->robustness_variable * i->query_interval
|
|
+ ipmcp.config.query_response_interval / 2;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_handle_query
|
|
*===========================================================================
|
|
* Description: Handle a received MLD QUERY message
|
|
* Parameters: netif - The interface the query was received at.
|
|
* query - The query message.
|
|
* src - The sender of the message.
|
|
* version - The version of the query
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_handle_query(Ipmcp_netif_t *netif,
|
|
Ipmcp_mld_query_pkt_t *query,
|
|
struct Ip_in6_addr *from,
|
|
int version)
|
|
{
|
|
Ipmcp_mld_netif_t *i = &netif->mld;
|
|
unsigned x;
|
|
Ipmcp_mld_maddr_t *maddr_obj;
|
|
|
|
IPCOM_LOG4(DEBUG, "MLDv%d membership query received from %s group %s on %s",
|
|
version,
|
|
ipcom_inet_ntop(IP_AF_INET6, from, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET6, &query->group_address,
|
|
ipmcp.log_buf + IP_INET6_ADDRSTRLEN,
|
|
sizeof(ipmcp.log_buf) - IP_INET6_ADDRSTRLEN),
|
|
netif->ifname);
|
|
|
|
maddr_obj = ipcom_hash_get(netif->mld.maddr_head, &query->group_address);
|
|
|
|
/*
|
|
* RFC 3810
|
|
*
|
|
* When a router receives a query with a lower IPv6 address than
|
|
* its own, it sets the Other Querier Present timer to Other
|
|
* Querier Present Timeout; if it was previously in Querier state,
|
|
* it switches to Non- Querier state and ceases to send queries on
|
|
* the link. After the Other Querier Present timer expires, it
|
|
* should re-enter the Querier state and begin sending General
|
|
* Queries.
|
|
*/
|
|
for (x = 0; x < sizeof(struct Ip_in6_addr); x++)
|
|
{
|
|
if (i->link_local_address.in6.addr8[x] == from->in6.addr8[x])
|
|
continue;
|
|
|
|
if (i->link_local_address.in6.addr8[x] < from->in6.addr8[x])
|
|
/*
|
|
* This node has the lower IP-address, ignore the query
|
|
*/
|
|
return;
|
|
}
|
|
|
|
if (i->state != IPMCP_LINK_STATE_NON_QUERIER)
|
|
{
|
|
if (maddr_obj != IP_NULL && IPCOM_TMO2_PENDING(&maddr_obj->specific_query_pending_timer))
|
|
/* Do not exit query state while specific query is pending */
|
|
return;
|
|
|
|
/*
|
|
* Become a non-querier on this link
|
|
*
|
|
* RFC 3810 - When a router receives a query with a lower IPv6
|
|
* address than its own, it sets the Other Querier Present
|
|
* timer to Other Querier Present Timeout; if it was
|
|
* previously in Querier state, it switches to Non-Querier
|
|
* state and ceases to send queries on the link.
|
|
*/
|
|
|
|
IPCOM_LOG1(NOTICE, "MLD: Link state NON-QUERIER on %s", netif->ifname);
|
|
i->state = IPMCP_LINK_STATE_NON_QUERIER;
|
|
/* It would be enough just to remove this interface as an outgoing interface
|
|
from all multicast routes, but all routes are flushed for simplicity.
|
|
State transition to/from QUERIER should be uncommon.
|
|
RFC4605 chapter 3
|
|
...
|
|
When the proxy device receives a packet destined for a multicast
|
|
group (channel in Source-Specific Multicast (SSM)), it uses a list
|
|
consisting of the upstream interface and any downstream interface
|
|
that has a subscription pertaining to this packet and on which it is
|
|
the MLD Querier.
|
|
...
|
|
*/
|
|
ipmcp_mld_flush_all_mroutes();
|
|
}
|
|
|
|
if (version == 2)
|
|
{
|
|
/* RFC3810 - 5.1.8
|
|
Routers adopt the QRV value from the most recently received Query as
|
|
their own [Robustness Variable] value, unless that most recently
|
|
received QRV was zero, in which case they use the default [Robustness
|
|
Variable] value specified in section 9.1, or a statically configured
|
|
value.
|
|
*/
|
|
if (IPMCP_MLDV2_GET_QRV(query))
|
|
netif->mld.robustness_variable = IPMCP_MLDV2_GET_QRV(query);
|
|
else
|
|
netif->mld.robustness_variable = ipmcp.config.robustness_variable;
|
|
|
|
/* RFC3810 - 5.1.9
|
|
Multicast routers that are not the current Querier adopt the QQI
|
|
value from the most recently received Query as their own [Query
|
|
Interval] value, unless that most recently received QQI was zero, in
|
|
which case the receiving routers use the default [Query Interval]
|
|
value specified in section 9.2.
|
|
*/
|
|
if (query->qqic)
|
|
netif->mld.query_interval = ipmcp_qqic_to_msec(query->qqic);
|
|
else
|
|
netif->mld.query_interval = ipmcp.config.query_interval;
|
|
|
|
if (IP_BIT_ISFALSE(query->res_s_qrv, IPMCP_MLDV2_QUERY_SUPRESS_BIT)
|
|
&& IP_NULL != maddr_obj)
|
|
{
|
|
/* When a router sends or receives a query with a clear Suppress
|
|
Router-Side Processing flag, it must update its timers to reflect the
|
|
correct timeout values for the group or sources being queried. The
|
|
following table describes the timer actions when sending or receiving
|
|
a Group-Specific or Group-and-Source Specific Query with the Suppress
|
|
Router-Side Processing flag not set.
|
|
|
|
Query Action
|
|
----- ------
|
|
Q(G,A) Source Timer for sources in A are lowered to LMQT
|
|
Q(G) Group Timer is lowered to LMQT
|
|
*/
|
|
|
|
if (query->num_sources == 0)
|
|
maddr_obj->base.ops->set_group_timer(&maddr_obj->base, ipmcp_last_member_query_time());
|
|
else
|
|
{
|
|
Ipmcp_mld_saddr_t *saddr_obj;
|
|
unsigned s;
|
|
unsigned num_sources = ip_ntohs(query->num_sources);
|
|
|
|
for (s = 0; s < num_sources; s++)
|
|
{
|
|
saddr_obj = ipcom_hash_get(maddr_obj->base.include_set,
|
|
&query->source_addresses[s]);
|
|
if (saddr_obj != IP_NULL)
|
|
saddr_obj->base.ops->set_timer(&saddr_obj->base,
|
|
ipmcp_last_member_query_time());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(void)ipcom_tmo2_cancel(&i->timer);
|
|
(void)ipcom_tmo2_request(ipmcp.tmo_head,
|
|
&i->timer,
|
|
(Ipcom_tmo2_func)ipmcp_mld_resume_querier_tmo,
|
|
netif,
|
|
ipmcp_mld_other_querier_present_timeout(i));
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_handle_membership_report_rec
|
|
*===========================================================================
|
|
* Description: Handles v2 membership report record for an address.
|
|
* Parameters: netif - The interface the query was received at.
|
|
* rec - The membership report record.
|
|
* version - The original version, this could be a message that
|
|
* has been translated to its correspoding MLDv2 record.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_handle_membership_report_rec(Ipmcp_netif_t *netif,
|
|
Ipmcp_mld_group_rec_pkt_t *rec,
|
|
int version)
|
|
{
|
|
Ipmcp_mld_maddr_t *maddr_obj;
|
|
Ip_bool new_entry = IP_FALSE;
|
|
Ipmcp_mld_rec_t rec_obj;
|
|
|
|
if (IP_IN6_IS_ADDR_MULTICAST(&rec->group_address) == IP_FALSE
|
|
|| IPMCP_MLD_IS_LINK_LOCAL_MCAST_ADDR(&rec->group_address))
|
|
/* Never forward link local multicast addresses */
|
|
return;
|
|
|
|
maddr_obj = ipcom_hash_get(netif->mld.maddr_head, &rec->group_address);
|
|
if (maddr_obj == IP_NULL)
|
|
{
|
|
/* Initialize a new address object */
|
|
maddr_obj = ipcom_calloc(1, sizeof(*maddr_obj));
|
|
if (maddr_obj == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipmcp_mld_handle_membership_report_rec: ipcom_calloc failed");
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
maddr_obj->base.include_set =
|
|
ipcom_hash_new((Ipcom_hash_obj_func)ipmcp_mld_saddr_obj_hash,
|
|
(Ipcom_hash_key_func)ipmcp_mld_addr_key_hash,
|
|
(Ipcom_hash_cmp_func)ipmcp_mld_saddr_compare);
|
|
maddr_obj->base.exclude_set =
|
|
ipcom_hash_new((Ipcom_hash_obj_func)ipmcp_mld_saddr_obj_hash,
|
|
(Ipcom_hash_key_func)ipmcp_mld_addr_key_hash,
|
|
(Ipcom_hash_cmp_func)ipmcp_mld_saddr_compare);
|
|
if (maddr_obj->base.include_set == IP_NULL || maddr_obj->base.exclude_set == IP_NULL)
|
|
{
|
|
ipcom_hash_delete(maddr_obj->base.include_set);
|
|
ipcom_hash_delete(maddr_obj->base.exclude_set);
|
|
ipcom_free(maddr_obj);
|
|
IPCOM_LOG0(ERR, "ipmcp_mld_handle_membership_report_rec: failed to create hash table");
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
maddr_obj->maddr = rec->group_address;
|
|
maddr_obj->base.netif = netif;
|
|
maddr_obj->base.mode_is_include = IP_TRUE;
|
|
maddr_obj->base.ops = &ipmcp.mld.maddr_ops;
|
|
maddr_obj->base.domain = IP_AF_INET6;
|
|
maddr_obj->base.addr_len = sizeof(struct Ip_in6_addr);
|
|
|
|
IPCOM_TMO2_INIT(&maddr_obj->base.query_timer);
|
|
IPCOM_TMO2_INIT(&maddr_obj->base.group_timer);
|
|
IPCOM_TMO2_INIT(&maddr_obj->v1_host_present_timer);
|
|
IPCOM_TMO2_INIT(&maddr_obj->specific_query_pending_timer);
|
|
|
|
new_entry = IP_TRUE;
|
|
}
|
|
|
|
if (version == 1)
|
|
{
|
|
/* MLDv1 host report, start compatibility timer */
|
|
if (IPCOM_TMO2_PENDING(&maddr_obj->v1_host_present_timer) == IP_FALSE)
|
|
IPCOM_LOG1(INFO, "MLD: Enter MLDv1 compatibility mode on %s", netif->ifname);
|
|
(void)ipcom_tmo2_cancel(&maddr_obj->v1_host_present_timer);
|
|
(void)ipcom_tmo2_request(ipmcp.tmo_head,
|
|
&maddr_obj->v1_host_present_timer,
|
|
(Ipcom_tmo2_func)ipmcp_mld_version_1_compat_tmo,
|
|
maddr_obj,
|
|
netif->mld.robustness_variable
|
|
* netif->mld.query_interval
|
|
+ ipmcp.config.query_response_interval);
|
|
}
|
|
|
|
rec_obj.base.ops = &ipmcp.mld.rec_ops;
|
|
rec_obj.rec = rec;
|
|
|
|
if (ipmcp_handle_membership_report_rec(&maddr_obj->base, &rec_obj.base) != 0)
|
|
goto cleanup;
|
|
|
|
if (new_entry)
|
|
{
|
|
if (maddr_obj->base.mode_is_include == IP_TRUE
|
|
&& maddr_obj->base.include_set->elem == 0)
|
|
/* IS_IN, ALLOW, BLOCK or TO_IN with empty set, still no listeners */
|
|
goto cleanup;
|
|
|
|
if (ipcom_hash_add(netif->mld.maddr_head, maddr_obj) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_free(maddr_obj);
|
|
IPCOM_LOG0(ERR, "ipmcp_mld_handle_membership_report_rec: ipcom_hash_addr failed");
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
IPCOM_LOG2(DEBUG, "MLD: Created entry for %s on %s",
|
|
ipcom_inet_ntop(IP_AF_INET6, &maddr_obj->maddr, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
netif->ifname);
|
|
}
|
|
|
|
ipmcp_mld_update_upstream_if_filter(maddr_obj);
|
|
return;
|
|
|
|
cleanup:
|
|
if (new_entry && maddr_obj != IP_NULL)
|
|
{
|
|
ipcom_hash_delete(maddr_obj->base.include_set);
|
|
ipcom_hash_delete(maddr_obj->base.exclude_set);
|
|
if (maddr_obj->base.report_set != IP_NULL)
|
|
{
|
|
ipcom_set_delete(maddr_obj->base.report_set);
|
|
}
|
|
|
|
(void)ipcom_tmo2_cancel(&maddr_obj->v1_host_present_timer);
|
|
ipcom_free(maddr_obj);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_handle_listener_report
|
|
*===========================================================================
|
|
* Description: Handle a MLD listener report message.
|
|
* Parameters: netif - The interface the query was received at.
|
|
* report - The membership report message.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_handle_listener_report(Ipmcp_netif_t *netif, Ipmcp_mld_pkt_t *report)
|
|
{
|
|
Ipmcp_mld_group_rec_pkt_t rec;
|
|
|
|
/* RFC3810 - 8.3.2: MLDv1 report should be processed as IS_EX( {} ) */
|
|
ipcom_memset(&rec, 0, sizeof(rec));
|
|
rec.type = IPMCP_GROUP_REC_TYPE_MODE_IS_EXCLUDE;
|
|
rec.group_address = report->group_address;
|
|
ipmcp_mld_handle_membership_report_rec(netif, &rec, 1);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_handle_listener_done
|
|
*===========================================================================
|
|
* Description: Handle a MLD listener done message.
|
|
* Parameters: .
|
|
* Returns: .
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_handle_listener_done(Ipmcp_netif_t *netif, Ipmcp_mld_pkt_t* report)
|
|
{
|
|
Ipmcp_mld_group_rec_pkt_t rec;
|
|
|
|
/* RFC3810 - 8.3.2: MLDv1 done should be processed as TO_IN( {} ) */
|
|
ipcom_memset(&rec, 0, sizeof(rec));
|
|
rec.type = IPMCP_GROUP_REC_TYPE_MODE_CHANGE_TO_INCLUDE_MODE;
|
|
rec.group_address = report->group_address;
|
|
ipmcp_mld_handle_membership_report_rec(netif, &rec, 1);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_activate
|
|
*===========================================================================
|
|
* Description: Activate MLD server on specified interface.
|
|
* Parameters: .
|
|
* Returns: .
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_activate(Ipmcp_netif_t *netif)
|
|
{
|
|
struct Ip_ipv6_mreq mreq;
|
|
|
|
/* Enter promiscuous mode on this interface */
|
|
ipcom_memset(&mreq.ipv6mr_multiaddr, 0, sizeof(struct Ip_in6_addr));
|
|
mreq.ipv6mr_interface = netif->ifindex;
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_IPV6_JOIN_GROUP,
|
|
&mreq, sizeof(mreq)) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipmcp_mld_activate: setsockopt IPPROTO_IPV6, IPV6_JOIN_GROUP failed on %s: %s",
|
|
ipcom_if_indextoname(netif->ifindex, ipmcp.log_buf), ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
/* Start sending general queries */
|
|
(void)ipmcp_mld_resume_querier_tmo(netif);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_deactivate
|
|
*===========================================================================
|
|
* Description: Dectivate MLD server on specified interface.
|
|
* Parameters: .
|
|
* Returns: .
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_deactivate(Ipmcp_netif_t *netif)
|
|
{
|
|
struct Ip_ipv6_mreq mreq;
|
|
Ipmcp_mld_netif_t *i = &netif->mld;
|
|
|
|
/* Cancel downstream interface timer */
|
|
(void)ipcom_tmo2_cancel(&i->timer);
|
|
|
|
if (i->maddr_head)
|
|
ipcom_hash_for_each(i->maddr_head,
|
|
(Ipcom_hash_foreach_cb_func)ipmcp_mld_maddr_drop,
|
|
netif);
|
|
|
|
if (netif->mld.state != IPMCP_LINK_STATE_INACTIVE)
|
|
{
|
|
/* Leave promiscuous mode on this interface */
|
|
ipcom_memset(&mreq.ipv6mr_multiaddr, 0, sizeof(struct Ip_in6_addr));
|
|
mreq.ipv6mr_interface = netif->ifindex;
|
|
(void)ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_IPV6_LEAVE_GROUP,
|
|
&mreq, sizeof(mreq));
|
|
}
|
|
|
|
/* Change state */
|
|
IPCOM_LOG1(NOTICE, "MLD: Link state INACTIVE on %s", netif->ifname);
|
|
netif->mld.state = IPMCP_LINK_STATE_INACTIVE;
|
|
}
|
|
|
|
|
|
/* ########### Ipmcp_mld_rec_t methods ########### */
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_rec_get_type
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u8
|
|
ipmcp_mld_rec_get_type(Ipmcp_mld_rec_t *self)
|
|
{
|
|
return self->rec->type;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_rec_get_maddr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_mld_rec_get_maddr(Ipmcp_mld_rec_t *self)
|
|
{
|
|
return &self->rec->group_address;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_rec_get_num_saddrs
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_mld_rec_get_num_saddrs(Ipmcp_mld_rec_t *self)
|
|
{
|
|
return ip_ntohs(self->rec->num_sources);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_rec_get_saddr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_mld_rec_get_saddr(Ipmcp_mld_rec_t *self, unsigned i)
|
|
{
|
|
ip_assert(ipmcp_mld_rec_get_num_saddrs(self) > i);
|
|
return &self->rec->source_addresses[i];
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_rec_as_set
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipcom_set *
|
|
ipmcp_mld_rec_as_set(Ipmcp_mld_rec_t *self)
|
|
{
|
|
Ipcom_set *set;
|
|
unsigned s;
|
|
unsigned num = ip_ntohs(self->rec->num_sources);
|
|
|
|
set = ipcom_set_new(sizeof(struct Ip_in6_addr));
|
|
if (set == IP_NULL)
|
|
return IP_NULL;
|
|
|
|
for (s = 0; s < num; s++)
|
|
if (ipcom_set_add(set, &self->rec->source_addresses[s]) == IPCOM_ERR_NO_MEMORY)
|
|
{
|
|
IP_PANIC2();
|
|
ipcom_set_delete(set);
|
|
return IP_NULL;
|
|
}
|
|
return set;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_rec_clear_source_list
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_rec_clear_source_list(Ipmcp_mld_rec_t *self)
|
|
{
|
|
self->rec->num_sources = 0;
|
|
}
|
|
|
|
|
|
/* ########### Ipmcp_mld_saddr_t methods ########### */
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_saddr_get_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_mld_saddr_get_addr(Ipmcp_mld_saddr_t *self)
|
|
{
|
|
return &self->saddr;
|
|
}
|
|
|
|
|
|
/* ########### Ipmcp_mld_daddr_t methods ########### */
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_delete
|
|
*===========================================================================
|
|
* Description: Drops a multicast address from a specified interface.
|
|
* Parameters: maddr_obj - The address to delete.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_maddr_delete(Ipmcp_mld_maddr_t *maddr_obj)
|
|
{
|
|
Ipmcp_netif_t *netif = maddr_obj->base.netif;
|
|
|
|
/* Remove from hash table */
|
|
(void)ipcom_hash_remove(netif->mld.maddr_head, maddr_obj);
|
|
ipmcp_mld_update_upstream_if_filter(maddr_obj);
|
|
|
|
/* Cancel timers */
|
|
(void)ipcom_tmo2_cancel(&maddr_obj->specific_query_pending_timer);
|
|
(void)ipcom_tmo2_cancel(&maddr_obj->v1_host_present_timer);
|
|
ipmcp_maddr_cleanup(&maddr_obj->base);
|
|
|
|
/* Free object */
|
|
ipcom_free(maddr_obj);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_drop
|
|
*===========================================================================
|
|
* Description: Drops a multicast address from a specified interface.
|
|
* Parameters: maddr_obj - The address to destroy.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_maddr_drop(Ipmcp_mld_maddr_t *maddr_obj, void *unused)
|
|
{
|
|
(void) unused;
|
|
ipmcp_mld_maddr_delete(maddr_obj);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_get_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_mld_maddr_get_addr(Ipmcp_mld_maddr_t *self)
|
|
{
|
|
return &self->maddr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_compatibility_mode
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmcp_mld_maddr_compatibility_mode(Ipmcp_mld_maddr_t *self)
|
|
{
|
|
if (IPCOM_TMO2_PENDING(&self->v1_host_present_timer))
|
|
return IPMCP_MCAST_COMPAT_IGMPV2_MLDV1;
|
|
return IPMCP_MCAST_COMPAT_IGMPV3_MLDV2;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_add_saddr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipmcp_mld_saddr_t *
|
|
ipmcp_mld_maddr_add_saddr(Ipmcp_mld_maddr_t *self,
|
|
struct Ip_in6_addr *saddr,
|
|
Ip_bool is_include_set)
|
|
{
|
|
Ipmcp_mld_saddr_t *saddr_obj;
|
|
|
|
saddr_obj = ipcom_calloc(1, sizeof(*saddr_obj));
|
|
saddr_obj->base.ops = &ipmcp.mld.saddr_ops;
|
|
saddr_obj->saddr = *saddr;
|
|
saddr_obj->base.maddr = &self->base;
|
|
IPCOM_TMO2_INIT(&saddr_obj->base.timer);
|
|
|
|
if (ipcom_hash_add(is_include_set ? self->base.include_set : self->base.exclude_set,
|
|
saddr_obj) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_free(saddr_obj);
|
|
return IP_NULL;
|
|
}
|
|
return saddr_obj;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_del_saddrs_not_in_rec_cb
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_maddr_del_saddrs_not_in_rec_cb(Ipmcp_mld_saddr_t *saddr,
|
|
Ipmcp_mld_rec_t *rec)
|
|
{
|
|
unsigned s, num = ip_ntohs(rec->rec->num_sources);
|
|
for (s = 0; s < num; s++)
|
|
if (IP_IN6_ARE_ADDR_EQUAL(&rec->rec->source_addresses[s], &saddr->saddr))
|
|
return;
|
|
saddr->base.ops->dtor(&saddr->base);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_del_saddrs_not_in_rec
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_maddr_del_saddrs_not_in_rec(Ipmcp_mld_maddr_t *self,
|
|
Ipmcp_mld_rec_t *rec,
|
|
Ip_bool is_include_set)
|
|
{
|
|
ipcom_hash_for_each(is_include_set ? self->base.include_set : self->base.exclude_set,
|
|
(Ipcom_hash_foreach_cb_func) ipmcp_mld_maddr_del_saddrs_not_in_rec_cb,
|
|
rec);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_send_query
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_mld_maddr_send_query(Ipmcp_mld_maddr_t *self,
|
|
Ip_u32 max_response_interval_msec,
|
|
Ipcom_set *saddrs)
|
|
{
|
|
if (self->base.netif->mld.state != IPMCP_LINK_STATE_QUERIER)
|
|
return;
|
|
|
|
(void)ipcom_tmo2_cancel(&self->specific_query_pending_timer);
|
|
(void)ipcom_tmo2_request(ipmcp.tmo_head,
|
|
&self->specific_query_pending_timer,
|
|
(Ipcom_tmo2_func)ipmcp_mld_specific_query_tmo,
|
|
self,
|
|
max_response_interval_msec);
|
|
|
|
ipmcp_mld_query_send(self->base.netif,
|
|
&self->maddr,
|
|
max_response_interval_msec,
|
|
(saddrs == IP_NULL
|
|
? self->base.query_suppress_router_side_processing
|
|
: self->base.source_query_suppress_router_side_processing),
|
|
saddrs);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_maddr_group_membership_interval
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u32
|
|
ipmcp_mld_maddr_group_membership_interval(Ipmcp_maddr_t *self)
|
|
{
|
|
/* The Group Membership Interval is the amount of time that must pass
|
|
before a multicast router decides there are no more members of a
|
|
group or a particular source on a network.
|
|
|
|
This value MUST be ((the Robustness Variable) times (the Query
|
|
Interval)) plus (one Query Response Interval).
|
|
*/
|
|
return self->netif->mld.robustness_variable * self->netif->mld.query_interval
|
|
+ ipmcp.config.query_response_interval;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_is_msg_ok
|
|
*===========================================================================
|
|
* Description: Basic sanity check on the incoming message.
|
|
* Parameters: version - MLD version of the incoming message.
|
|
* Returns: IP_TRUE - message passed basic sanity.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_mld_is_msg_ok(int version)
|
|
{
|
|
return version <= ipcom_sysvar_get_as_int("ipmcp.mld.CompatibilityMode", 2);
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_mroute_input
|
|
*===========================================================================
|
|
* Description: Receive ICMPv6 message from multicast forwarding engine.
|
|
* Parameters: .
|
|
* Returns: .
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_mld_mroute_input(int fd)
|
|
{
|
|
int length;
|
|
struct Ip_mrt6msg *mrt6msg;
|
|
char buf[sizeof(struct Ip_mrt6msg)];
|
|
|
|
length = ipcom_recv(fd,
|
|
buf,
|
|
sizeof(buf),
|
|
0);
|
|
|
|
if (length < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "MLD: ipcom_socketread() failed: %s", ipcom_strerror(ipcom_errno));
|
|
return -1;
|
|
}
|
|
|
|
if (ipmcp.state != IPMCP_STATE_RUNNING)
|
|
/* Uplink interface not active */
|
|
return 0;
|
|
|
|
/* Too short packet */
|
|
if (length < (int) sizeof(*mrt6msg))
|
|
return 0;
|
|
|
|
mrt6msg = (struct Ip_mrt6msg*)buf;
|
|
IPCOM_LOG2(INFO, "MLD: Got mroute message %s --> %s",
|
|
ipcom_inet_ntop(IP_AF_INET6, &mrt6msg->im6_src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET6, &mrt6msg->im6_dst, ipmcp.log_buf + IP_INET6_ADDRSTRLEN, sizeof(ipmcp.log_buf)));
|
|
|
|
return ipmcp_mld_add_mroute(mrt6msg->im6_mif, &mrt6msg->im6_dst, &mrt6msg->im6_src);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_input
|
|
*===========================================================================
|
|
* Description: Receive MLD message
|
|
* Parameters: .
|
|
* Returns: .
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_mld_input(int fd)
|
|
{
|
|
struct Ip_msghdr m;
|
|
struct Ip_iovec iov;
|
|
int len;
|
|
struct Ip_sockaddr_in6 from;
|
|
Ipmcp_mld_pkt_t *pkt;
|
|
Ipmcp_mld_query_pkt_t *query;
|
|
Ipmcp_mld_report_pkt_t *report;
|
|
Ipmcp_mld_group_rec_pkt_t *next_rec;
|
|
Ipmcp_netif_t *netif;
|
|
struct Ip_in6_pktinfo *pktinfo = IP_NULL;
|
|
int *hoplimit = IP_NULL;
|
|
struct Ip_ip6_hbh *hbh = IP_NULL;
|
|
Ip_socklen_t rt_alert_len;
|
|
Ip_u16 *rt_alert_opt;
|
|
struct Ip_cmsghdr *c;
|
|
Ip_u8 cmsg_buf[IP_CMSG_SPACE(sizeof(struct Ip_in6_pktinfo))
|
|
+ IP_CMSG_SPACE(sizeof(int))
|
|
+ IP_CMSG_SPACE(8 /* HBH-opts */)];
|
|
int v;
|
|
int num_rec;
|
|
|
|
iov.iov_base = ipmcp.recv_buf;
|
|
iov.iov_len = sizeof(ipmcp.recv_buf);
|
|
|
|
m.msg_name = &from;
|
|
m.msg_namelen = sizeof(from);
|
|
m.msg_iov = &iov;
|
|
m.msg_iovlen = 1;
|
|
m.msg_control = cmsg_buf;
|
|
m.msg_controllen = sizeof(cmsg_buf);
|
|
m.msg_flags = 0;
|
|
|
|
len = ipcom_recvmsg(fd, &m, 0);
|
|
if (len < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "MLD: ipcom_recvmsg() failed: %s", ipcom_strerror(ipcom_errno));
|
|
return -1;
|
|
}
|
|
|
|
/* Too short packet */
|
|
if (len < (int) sizeof(Ipmcp_mld_pkt_t))
|
|
return 0;
|
|
|
|
pkt = iov.iov_base;
|
|
|
|
/* Discard packets with non link-local sender address */
|
|
if (!IP_IN6_IS_ADDR_LINK_LOCAL(&from.sin6_addr))
|
|
{
|
|
if(pkt->ipmcp_mld_type == IPMCP_MLD_LISTENER_QUERY)
|
|
{
|
|
/* RFC3590 ...if a node receives a query message with an IPv6 source address
|
|
* set to the unspecified address, it MUST silently discard the message and
|
|
* SHOULD log a warning */
|
|
IPCOM_LOG1(NOTICE, "MLD Query message received from non link-local address %s",
|
|
ipcom_inet_ntop(IP_AF_INET6, &from.sin6_addr, ipmcp.log_buf, sizeof(ipmcp.log_buf)));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
for (c = IP_CMSG_FIRSTHDR(&m);
|
|
c != IP_NULL;
|
|
c = IP_CMSG_NXTHDR(&m, c))
|
|
{
|
|
if (c->cmsg_level == IP_IPPROTO_IPV6)
|
|
{
|
|
switch (c->cmsg_type)
|
|
{
|
|
case IP_IPV6_PKTINFO:
|
|
pktinfo = IP_CMSG_DATA(c);
|
|
break;
|
|
case IP_IPV6_HOPLIMIT:
|
|
hoplimit = IP_CMSG_DATA(c);
|
|
break;
|
|
case IP_IPV6_HOPOPTS:
|
|
hbh = IP_CMSG_DATA(c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pktinfo == IP_NULL || hoplimit == IP_NULL || hbh == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipmcp_mld_input: failed to get pktinfo, hoplimit and/or hop-by-hop ext");
|
|
return -1;
|
|
}
|
|
|
|
if (IP_GET_32ON8(hoplimit) != 1)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipmcp_mld_input: hoplimit != 1");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Does this message include a hop-by-hop extension header with a
|
|
* route alert option?
|
|
*/
|
|
if (ipcom_inet6_opt_find(hbh,
|
|
(hbh->ip6h_len + 1) << 3,
|
|
0,
|
|
IP_IP6OPT_ROUTER_ALERT,
|
|
&rt_alert_len,
|
|
(void**)&rt_alert_opt) == -1
|
|
|| rt_alert_len != sizeof(*rt_alert_opt)
|
|
|| *rt_alert_opt != IP_IP6_ALERT_MLD)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipmcp_mld_input: missing or invalid router alert option");
|
|
return -1;
|
|
}
|
|
|
|
netif = ipmcp_if_find(pktinfo->ipi6_ifindex);
|
|
if (IP_NULL == netif || netif->mld.state == IPMCP_LINK_STATE_INACTIVE)
|
|
/* Packet received on one of the interface where we are not acting as a router */
|
|
return 0;
|
|
|
|
/* Discard packets from ourselves or packets not sent to a multicast address */
|
|
if (IP_IN6_ARE_ADDR_EQUAL(&netif->mld.link_local_address, &from.sin6_addr))
|
|
return 0;
|
|
|
|
pkt = iov.iov_base;
|
|
|
|
if (!IP_IN6_IS_ADDR_MULTICAST(&pktinfo->ipi6_addr))
|
|
{
|
|
struct Ip_ifaddrs *ifaddr;
|
|
struct Ip_ifaddrs *ifaddrs;
|
|
|
|
ipcom_getifaddrs(&ifaddrs);
|
|
|
|
for (ifaddr = ifaddrs; ifaddr != IP_NULL; ifaddr = ifaddr->ifa_next)
|
|
{
|
|
if (ifaddr->ifa_addr->sa_family == IP_AF_INET6 && ipcom_if_nametoindex(ifaddr->ifa_name) == pktinfo->ipi6_ifindex)
|
|
{
|
|
struct Ip_sockaddr_in6 *s_in = (struct Ip_sockaddr_in6 *) (ifaddr->ifa_addr);
|
|
|
|
if (IP_IN6_ARE_ADDR_EQUAL(&s_in->sin6_addr, &pktinfo->ipi6_addr))
|
|
break;
|
|
}
|
|
}
|
|
|
|
ipcom_freeifaddrs(ifaddrs);
|
|
|
|
/* Unicast address on this interface? */
|
|
if (ifaddr == IP_NULL)
|
|
return 0;
|
|
}
|
|
|
|
pkt = iov.iov_base;
|
|
|
|
switch (pkt->ipmcp_mld_type)
|
|
{
|
|
case IPMCP_MLD_LISTENER_QUERY:
|
|
query = iov.iov_base;
|
|
if (len == IPMCP_MLD_MIN_PKT_LEN)
|
|
v = 1;
|
|
else
|
|
{
|
|
if (len < IPMCP_MLD_MIN_PKT_LEN + 4 + ip_htons(query->num_sources) * 16)
|
|
/* Invalid query, ignore this message */
|
|
return 0;
|
|
v = 2;
|
|
}
|
|
if (ipmcp_mld_is_msg_ok(v))
|
|
ipmcp_mld_handle_query(netif, query, &from.sin6_addr, v);
|
|
break;
|
|
|
|
case IPMCP_MLD_LISTENER_REPORT:
|
|
if (ipmcp_mld_is_msg_ok(1))
|
|
ipmcp_mld_handle_listener_report(netif, pkt);
|
|
break;
|
|
|
|
case IPMCP_MLD_LISTENER_REDUCTION:
|
|
if (ipmcp_mld_is_msg_ok(1))
|
|
ipmcp_mld_handle_listener_done(netif, pkt);
|
|
break;
|
|
|
|
case IPMCP_MLD_LISTENER_REPORT_V2:
|
|
if (!ipmcp_mld_is_msg_ok(2))
|
|
return 0;
|
|
|
|
report = iov.iov_base;
|
|
/* Size of mandatory message part */
|
|
len -= 8;
|
|
num_rec = ip_ntohs(report->num_group_rec);
|
|
next_rec = report->recs;
|
|
for (v = 0; v < num_rec; v++)
|
|
{
|
|
Ipmcp_mld_group_rec_pkt_t *rec = next_rec;
|
|
int rec_len;
|
|
|
|
rec_len = 20 + ip_ntohs(rec->num_sources) * 16 + rec->aux_data_len * 4;
|
|
if (len < rec_len)
|
|
{
|
|
IPCOM_LOG2(NOTICE,
|
|
"MLDv2 invalid membership report received from %s on %s",
|
|
ipcom_inet_ntop(IP_AF_INET6, &from.sin6_addr, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
netif->ifname);
|
|
/* Ignore this packet */
|
|
return 0;
|
|
}
|
|
len -= rec_len;
|
|
next_rec = (Ipmcp_mld_group_rec_pkt_t *) ((Ip_u8 *) rec + rec_len);
|
|
|
|
ipmcp_mld_handle_membership_report_rec(netif, rec, 2);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
IPCOM_LOG1(ERR, "MLD got message of unkown MLD type %d", pkt->ipmcp_mld_type);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_addr_change
|
|
*===========================================================================
|
|
* Description: An address was added or removed.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmcp_mld_addr_change(Ipmcp_netif_t *netif, Ip_bool add)
|
|
{
|
|
Ipmcp_mld_netif_t *i = &netif->mld;
|
|
|
|
if (add)
|
|
{
|
|
/* Always update link-local address */
|
|
if (ipmcp_mld_get_link_local_addr(netif->ifindex, &i->link_local_address)
|
|
== IPMCP_LINK_LOCAL_ADR_AVAIL)
|
|
if (netif->mld.state == IPMCP_LINK_STATE_INACTIVE)
|
|
{
|
|
/* Our link local has now been set and isn't tentative. Activate! */
|
|
ipmcp_mld_activate(netif);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (i->state != IPMCP_LINK_STATE_INACTIVE
|
|
&& (ipmcp_mld_get_link_local_addr(netif->ifindex, &i->link_local_address)
|
|
== IPMCP_LINK_LOCAL_ADR_NOTAVAIL))
|
|
/* Our link-local address has been removed. Deactivate! */
|
|
ipmcp_mld_deactivate(netif);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_if_create
|
|
*===========================================================================
|
|
* Description: Creates the MLD part of an interface.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_mld_if_create(Ipmcp_netif_t *netif)
|
|
{
|
|
struct Ip_mif6ctl mifctl;
|
|
Ipmcp_mld_data_t *d = &ipmcp.mld;
|
|
Ipmcp_mld_netif_t *i = &netif->mld;
|
|
int x;
|
|
|
|
for (x = 0; x < IP_MAXMIFS; x++)
|
|
{
|
|
d->last_mifi = (Ip_mifi_t) ((d->last_mifi + 1) % IP_MAXMIFS);
|
|
i->mif_index = d->last_mifi;
|
|
|
|
/* Register MIF in forwarding engine */
|
|
mifctl.mif6c_mifi = netif->mld.mif_index;
|
|
mifctl.mif6c_flags = 0;
|
|
mifctl.mif6c_pifi = (Ip_u16)netif->ifindex;
|
|
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MRT6_ADD_MIF,
|
|
&mifctl,
|
|
sizeof(mifctl)) >= 0)
|
|
break;
|
|
}
|
|
|
|
if (x == IP_MAXMIFS)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_mld_if_create: "
|
|
"ipcom_setsockopt IP_MRT6_ADD_MIF %s", ipcom_strerror(ipcom_errno));
|
|
return -1;
|
|
}
|
|
|
|
i->startup_query_count = ipmcp.config.startup_query_count;
|
|
|
|
IPCOM_TMO2_INIT(&i->timer);
|
|
|
|
/* Create multicast address database on this interface */
|
|
i->maddr_head = ipcom_hash_new((Ipcom_hash_obj_func)ipmcp_mld_maddr_obj_hash,
|
|
(Ipcom_hash_key_func)ipmcp_mld_addr_key_hash,
|
|
(Ipcom_hash_cmp_func)ipmcp_mld_maddr_compare);
|
|
|
|
ipmcp_mld_addr_change(netif, IP_TRUE);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_if_destroy
|
|
*===========================================================================
|
|
* Description: Destroy an interface descriptor for MLD
|
|
* Parameters: .
|
|
* Returns: .
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmcp_mld_if_destroy(Ipmcp_netif_t *netif)
|
|
{
|
|
struct Ip_mif6ctl mifctl;
|
|
|
|
ipmcp_mld_deactivate(netif);
|
|
if (netif->mld.maddr_head)
|
|
ipcom_hash_delete(netif->mld.maddr_head);
|
|
|
|
/* Unregister MIF in forwarding engine */
|
|
mifctl.mif6c_mifi = (Ip_mifi_t)netif->mld.mif_index;
|
|
mifctl.mif6c_flags = 0;
|
|
mifctl.mif6c_pifi = (Ip_u16)netif->ifindex;
|
|
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MRT6_DEL_MIF,
|
|
&mifctl,
|
|
sizeof(mifctl)) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_setsockopt IP_MRT6_DEL_MIF: %s", ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_if_create
|
|
*===========================================================================
|
|
* Description: Creates the MLD part of the upstream interface.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_mld_upstream_if_create(Ipmcp_upstream_netif_t *netif)
|
|
{
|
|
struct Ip_mif6ctl mifctl;
|
|
int var;
|
|
|
|
if (ipmcp_mld_get_link_local_addr(netif->ifindex,
|
|
&netif->mld.link_local_address) == IPMCP_LINK_LOCAL_ADR_NOTAVAIL)
|
|
{
|
|
IPCOM_LOG0(ERR, "MLD: Failed to get the link local address of the upstream interface");
|
|
return -1;
|
|
}
|
|
|
|
/* Create multicast address database on this interface */
|
|
netif->mld.maddr_head =
|
|
ipcom_hash_new((Ipcom_hash_obj_func)ipmcp_mld_upstream_maddr_obj_hash,
|
|
(Ipcom_hash_key_func)ipmcp_mld_addr_key_hash,
|
|
(Ipcom_hash_cmp_func)ipmcp_mld_upstream_maddr_compare);
|
|
|
|
if (netif->mld.maddr_head == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipmcp_mld_upstream_if_create: failed to create hash table");
|
|
return -1;
|
|
}
|
|
|
|
var = 1;
|
|
var = ipcom_setsockopt(ipmcp.mld.mrt_fd, IP_IPPROTO_IPV6, IP_MRT6_INIT, &var, sizeof(var));
|
|
if (var < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_mld_upstream_if_create: ipcom_setsockopt IP_MRT6_INIT", ipcom_errno);
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
netif->mld.mif_index = 1;
|
|
ipmcp.mld.last_mifi = 1;
|
|
|
|
/* Register MIF in forwarding engine */
|
|
ipcom_memset(&mifctl, 0, sizeof(mifctl));
|
|
mifctl.mif6c_mifi = netif->mld.mif_index;
|
|
mifctl.mif6c_pifi = (Ip_u16) netif->ifindex;
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd, IP_IPPROTO_IPV6,
|
|
IP_MRT6_ADD_MIF, &mifctl, sizeof(mifctl)) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_mld_upstream_if_create: ipcom_setsockopt IP_MRT6_ADD_MIF: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_upstream_if_destroy
|
|
*===========================================================================
|
|
* Description: Destroy an IPv6 upstream interface.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmcp_mld_upstream_if_destroy(Ipmcp_upstream_netif_t *netif)
|
|
{
|
|
struct Ip_mif6ctl mifctl;
|
|
int i;
|
|
|
|
/* Unregister VIF in forwarding engine */
|
|
ipcom_memset(&mifctl, 0, sizeof(mifctl));
|
|
mifctl.mif6c_mifi = netif->mld.mif_index;
|
|
mifctl.mif6c_pifi = (Ip_u16) netif->ifindex;
|
|
|
|
if (ipcom_setsockopt(ipmcp.mld.mrt_fd,
|
|
IP_IPPROTO_IPV6,
|
|
IP_MRT6_DEL_MIF,
|
|
&mifctl,
|
|
sizeof(mifctl)) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_setsockopt IP_MRT6_DEL_MIF: %s", ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
/* Destroy multicast elements in hash table */
|
|
ipcom_hash_for_each(netif->mld.maddr_head,
|
|
(Ipcom_hash_foreach_cb_func)ipmcp_mld_upstream_maddr_delete_cb,
|
|
IP_NULL);
|
|
ipcom_hash_delete(netif->mld.maddr_head);
|
|
ipcom_memset(&netif->mld, 0, sizeof(netif->mld));
|
|
|
|
i = 1;
|
|
i = ipcom_setsockopt(ipmcp.mld.mrt_fd, IP_IPPROTO_IPV6, IP_MRT6_DONE, &i, sizeof(i));
|
|
if (i < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_mld_exit: ipcom_setsockopt IP_MRT6_DONE: %s", ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_init
|
|
*===========================================================================
|
|
* Description: Initialize MLD.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_mld_init(void)
|
|
{
|
|
int var;
|
|
int ret;
|
|
struct Ip_icmp6_filter filter;
|
|
int offset;
|
|
Ip_u8 hbh_buf[IPMCP_ALIGNED_SIZE(sizeof(struct Ip_ip6_hbh)
|
|
+ sizeof(struct Ip_ip6_opt),
|
|
8)];
|
|
Ip_u16 *router_alert_val;
|
|
Ipmcp_mld_data_t *d = &ipmcp.mld;
|
|
|
|
(void)ipcom_inet_pton(IP_AF_INET6, "ff02::1", &ipmcp.mld.all_hosts_addr);
|
|
|
|
ipmcp.mld.mld_fd = ipcom_socket(IP_AF_INET6, IP_SOCK_RAW, IP_IPPROTO_ICMPV6);
|
|
if (ipmcp.mld.mld_fd == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_mld_init: ipcom_socket(AF_INET6,RAW) failed, errno = %d", ipcom_errno);
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
/* Enable receive of hop-limit */
|
|
var = 1;
|
|
ret = ipcom_setsockopt(ipmcp.mld.mld_fd, IP_IPPROTO_IPV6, IP_IPV6_RECVHOPLIMIT, &var, sizeof(var));
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_mld_init: ipcom_setsockopt IP_IPV6_RECVHOPLIMIT ", ipcom_errno);
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
ret = ipcom_setsockopt(ipmcp.mld.mld_fd, IP_IPPROTO_IPV6, IP_IPV6_RECVPKTINFO, &var, sizeof(var));
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_mld_init: ipcom_setsockopt IP_IPV6_RECVPKTINFO: %s", ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
ret = ipcom_setsockopt(ipmcp.mld.mld_fd, IP_IPPROTO_IPV6, IP_IPV6_RECVHOPOPTS, &var, sizeof(var));
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_mld_init: ipcom_setsockopt IP_IPV6_RECVHOPOPT: %s", ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
/* Setup icmp6 filter to block everything but MLD messages */
|
|
IP_ICMP6_FILTER_SETBLOCKALL(&filter);
|
|
IP_ICMP6_FILTER_SETPASS(IPMCP_MLD_LISTENER_QUERY, &filter);
|
|
IP_ICMP6_FILTER_SETPASS(IPMCP_MLD_LISTENER_REPORT, &filter);
|
|
IP_ICMP6_FILTER_SETPASS(IPMCP_MLD_LISTENER_REPORT_V2, &filter);
|
|
IP_ICMP6_FILTER_SETPASS(IPMCP_MLD_LISTENER_REDUCTION, &filter);
|
|
ret = ipcom_setsockopt(ipmcp.mld.mld_fd, IP_IPPROTO_ICMPV6, IP_ICMP6_FILTER, &filter, sizeof(filter));
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_setsockopt IP_ICMP6_FILTER ", ipcom_errno);
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
/* Create socket to receive multicast routing messages */
|
|
ipmcp.mld.mrt_fd = ipcom_socket(IP_AF_INET6, IP_SOCK_RAW, IP_IPPROTO_ICMPV6);
|
|
if (ipmcp.mld.mrt_fd == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipcom_socket(AF_INET6,RAW) failed, errno = %d", ipcom_errno);
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Setup icmp6 filter to block everything.
|
|
* Only internal messages from the MRT forwarding engine will pass through
|
|
*/
|
|
IP_ICMP6_FILTER_SETBLOCKALL(&filter);
|
|
ret = ipcom_setsockopt(ipmcp.mld.mrt_fd, IP_IPPROTO_ICMPV6, IP_ICMP6_FILTER, &filter, sizeof(filter));
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_setsockopt IP_ICMP6_FILTER: %s", ipcom_errno);
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
if ((offset = ipcom_inet6_opt_init(hbh_buf, sizeof(hbh_buf))) < 0)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipcom_inet6_opt_init() failed");
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
offset = ipcom_inet6_opt_append(hbh_buf,
|
|
sizeof(hbh_buf),
|
|
offset,
|
|
IP_IP6OPT_ROUTER_ALERT,
|
|
sizeof(Ip_u16),
|
|
2,
|
|
(void **) &router_alert_val);
|
|
if (offset < 0)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipcom_inet6_opt_append() failed");
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
*router_alert_val = IP_IP6_ALERT_MLD;
|
|
|
|
if ((offset = ipcom_inet6_opt_finish(hbh_buf, sizeof(hbh_buf), offset)) < 0)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipcom_inet6_opt_finish() failed");
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
ret = ipcom_setsockopt(ipmcp.mld.mld_fd, IP_IPPROTO_IPV6, IP_IPV6_HOPOPTS, hbh_buf, offset);
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_setsockopt IP_IPV6_HOPOPTS: %s", ipcom_errno);
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
/* Create the call table for the Ipmcp_mld_rec_t class */
|
|
d->rec_ops.get_type = (Ip_u8(*)(RTHIS)) ipmcp_mld_rec_get_type;
|
|
d->rec_ops.get_maddr = (void*(*)(RTHIS)) ipmcp_mld_rec_get_maddr;
|
|
d->rec_ops.get_num_saddrs = (unsigned(*)(RTHIS)) ipmcp_mld_rec_get_num_saddrs;
|
|
d->rec_ops.get_saddr = (void*(*)(RTHIS, unsigned)) ipmcp_mld_rec_get_saddr;
|
|
d->rec_ops.as_set = (Ipcom_set*(*)(RTHIS)) ipmcp_mld_rec_as_set;
|
|
d->rec_ops.clear_source_list = (void(*)(RTHIS)) ipmcp_mld_rec_clear_source_list;
|
|
|
|
/* Create the call table for the Ipmcp_mld_saddr_t class */
|
|
ipmcp_saddr_ops_init(&d->saddr_ops);
|
|
d->saddr_ops.get_addr = (void*(*)(STHIS)) ipmcp_mld_saddr_get_addr;
|
|
|
|
/* Create the call table for the Ipmcp_mld_maddr_t class */
|
|
ipmcp_maddr_ops_init(&d->maddr_ops);
|
|
d->maddr_ops.dtor = (void(*)(MTHIS)) ipmcp_mld_maddr_delete;
|
|
d->maddr_ops.get_addr = (void*(*)(MTHIS)) ipmcp_mld_maddr_get_addr;
|
|
d->maddr_ops.compatibility_mode = (int(*)(MTHIS)) ipmcp_mld_maddr_compatibility_mode;
|
|
d->maddr_ops.add_saddr = (Ipmcp_saddr_t*(*)(MTHIS, IP_CONST void*, Ip_bool)) ipmcp_mld_maddr_add_saddr;
|
|
d->maddr_ops.del_saddrs_not_in_rec = (void(*)(MTHIS, Ipmcp_rec_t*, Ip_bool)) ipmcp_mld_maddr_del_saddrs_not_in_rec;
|
|
d->maddr_ops.send_query = (void(*)(MTHIS, Ip_u32, Ipcom_set*)) ipmcp_mld_maddr_send_query;
|
|
d->maddr_ops.group_membership_interval = (Ip_u32(*)(MTHIS)) ipmcp_mld_maddr_group_membership_interval;
|
|
d->maddr_ops.update_upstream_if_filter = (void(*)(MTHIS)) ipmcp_mld_update_upstream_if_filter;
|
|
|
|
/* Create the call table for the Ipmcp_mld_upstream_maddr_t class */
|
|
d->upstream_maddr_ops.dtor = (void(*)(UTHIS))ipmcp_mld_upstream_maddr_delete;
|
|
d->upstream_maddr_ops.get_addr = (void*(*)(UTHIS)) ipmcp_mld_upstream_maddr_get_addr;
|
|
d->upstream_maddr_ops.change_mode = (void(*)(UTHIS, Ip_bool)) ipmcp_mld_upstream_maddr_change_mode;
|
|
d->upstream_maddr_ops.add_source = (void(*)(UTHIS, void*)) ipmcp_mld_upstream_maddr_add_source;
|
|
d->upstream_maddr_ops.del_source = (void(*)(UTHIS, void*)) ipmcp_mld_upstream_maddr_del_source;
|
|
|
|
/* Init success. */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_mld_exit
|
|
*===========================================================================
|
|
* Description: Frees resources held by the MLD module.
|
|
* Parameters: .
|
|
* Returns: .
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmcp_mld_exit(void)
|
|
{
|
|
(void)ipcom_socketclose(ipmcp.mld.mrt_fd);
|
|
ipmcp.mld.mrt_fd = IP_INVALID_SOCKET;
|
|
|
|
(void)ipcom_socketclose(ipmcp.mld.mld_fd);
|
|
ipmcp.mld.mld_fd = IP_INVALID_SOCKET;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
#else
|
|
/* Fix the "ISO C forbids an empty source file" warning */
|
|
int ipmcp_mld_empty_file;
|
|
#endif /* IPMCP_USE_MLD */
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|