2715 lines
91 KiB
C
2715 lines
91 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmcp_igmp.c,v $ $Revision: 1.36.10.7 $
|
|
* $Source: /home/interpeak/CVSRoot/ipmcp/src/ipmcp_igmp.c,v $
|
|
* $Author: hxu4 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipmcp_config.h"
|
|
|
|
#ifdef IPMCP_USE_IGMP
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_set.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sysvar.h>
|
|
#include <ipcom_sock2.h>
|
|
#include <ipcom_type.h>
|
|
|
|
#include "ipmcp_h.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPMCP_IGMP_TYPE_MEMBERSHIP_QUERY 0x11
|
|
#define IPMCP_IGMP_TYPE_MEMBERSHIP_REPORT 0x12
|
|
#define IPMCP_IGMP_TYPE_MEMBERSHIP_REPORT_V2 0x16
|
|
#define IPMCP_IGMP_TYPE_LEAVE_GROUP_V2 0x17
|
|
#define IPMCP_IGMP_TYPE_MEMBERSHIP_REPORT_V3 0x22
|
|
|
|
#define IPMCP_IGMP_MIN_PKT_LEN 8
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include <ipcom_align16.h>
|
|
typedef IP_PACKED1 struct Ipmcp_ip4_pkt_struct
|
|
{
|
|
Ip_u8 v_hl; /* 4bit version and 4bit header length. */
|
|
Ip_u8 tos; /* 8bit type of service. */
|
|
Ip_u16 len; /* Total length. */
|
|
Ip_u16 id; /* Identification. */
|
|
Ip_u16 off; /* Fragment offset field. */
|
|
Ip_u8 ttl; /* 8bit time-to-live. */
|
|
Ip_u8 p; /* 8bit IP protocol. */
|
|
Ip_u16 sum; /* Internet Checksum. */
|
|
Ip_u16 src[2]; /* Destination IP address. */
|
|
Ip_u16 dst[2]; /* Source IP address. */
|
|
}
|
|
IP_PACKED2 Ipmcp_ip4_pkt_t;
|
|
|
|
typedef IP_PACKED1 struct Ipmcp_pkt_ip_opt_struct
|
|
{
|
|
Ip_u8 flag_class_num; /* 1 bit copied flag, 2 bits class, 5 bits option number */
|
|
Ip_u8 len; /* Option length in octets included the header */
|
|
Ip_u8 data[2]; /* Option data */
|
|
}
|
|
IP_PACKED2 Ipmcp_pkt_ip_opt;
|
|
|
|
|
|
typedef IP_PACKED1 struct Ipmcp_igmp_query_pkt_struct
|
|
{
|
|
Ip_u8 type; /* One of the IPMCP_IGMP_TYPE_xxx constants */
|
|
Ip_u8 max_resp_time; /* The maximum response time in 1/10 of a second */
|
|
Ip_u16 checksum; /* Internet checksum */
|
|
struct Ip_in_addr group_address; /* IPv4 multicast address or 0 for general queries */
|
|
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_in_addr source_addresses[1]; /* Multicast source addresses */
|
|
|
|
}
|
|
IP_PACKED2 Ipmcp_igmp_query_pkt_t;
|
|
|
|
#define IPMCP_IGMPV3_QUERY_SUPRESS_BIT 0x08
|
|
#define IPMCP_IGMPV3_GET_QRV(query) ((query)->res_s_qrv & 0x07)
|
|
|
|
|
|
typedef IP_PACKED1 struct Ipmcp_igmp_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_in_addr group_address; /* IPv4 multicast address */
|
|
struct Ip_in_addr source_addresses[1]; /* Multicast source addresses */
|
|
}
|
|
IP_PACKED2 Ipmcp_igmp_group_rec_pkt_t;
|
|
|
|
typedef IP_PACKED1 struct Ipmcp_igmp_report_pkt_struct
|
|
{
|
|
Ip_u8 type; /* 0x22 */
|
|
Ip_u8 reserved;
|
|
Ip_u16 checksum; /* Internet checksum */
|
|
Ip_u16 reserved2;
|
|
Ip_u16 num_group_rec; /* Number of group records */
|
|
Ipmcp_igmp_group_rec_pkt_t recs[1];
|
|
}
|
|
IP_PACKED2 Ipmcp_igmp_report_pkt_t;
|
|
#include <ipcom_align16.h>
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipmcp_igmp_rec_t
|
|
*===========================================================================
|
|
* IGMPv3 record class
|
|
*/
|
|
typedef struct Ipmcp_igmp_rec_struct
|
|
{
|
|
Ipmcp_rec_t base; /* Base class */
|
|
Ipmcp_igmp_group_rec_pkt_t *rec; /* Pointer to an IGMPv3 record */
|
|
}
|
|
Ipmcp_igmp_rec_t;
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipmcp_igmp_saddr_struct
|
|
*===========================================================================
|
|
* IPv4 multicast source address descriptor for a downstream interface.
|
|
*/
|
|
typedef struct Ipmcp_igmp_saddr_struct
|
|
{
|
|
Ipmcp_saddr_t base; /* Base class */
|
|
struct Ip_in_addr saddr; /* Source address */
|
|
} Ipmcp_igmp_saddr_t;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC void ipmcp_igmp_maddr_drop(Ipmcp_igmp_maddr_t *maddr_obj, void *unused);
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_set_sock_addr
|
|
*===========================================================================
|
|
* Description: Creates a struct Ip_sockaddr_storage from a struct Ip_in_addr
|
|
* Parameters: s - A socket storage buffer.
|
|
* a - An inet address
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_set_sock_addr(struct Ip_sockaddr_storage *s, struct Ip_in_addr *a)
|
|
{
|
|
struct Ip_sockaddr_in *i = (struct Ip_sockaddr_in *)s;
|
|
|
|
ipcom_memset(i, 0, sizeof(*i));
|
|
i->sin_family = IP_AF_INET;
|
|
IPCOM_SA_LEN_SET(i, sizeof(*i));
|
|
i->sin_addr = *a;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_addr_key_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for an IPv4 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_igmp_addr_key_hash(struct Ip_in_addr *addr)
|
|
{
|
|
return ipcom_hash_update(addr, sizeof(*addr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_saddr_obj_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for the multicast source address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_igmp_saddr_obj_hash(Ipmcp_igmp_saddr_t *obj)
|
|
{
|
|
return ipcom_hash_update(&obj->saddr, sizeof(obj->saddr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_saddr_compare
|
|
*===========================================================================
|
|
* Description: Compares a MCP multicast source address entry with a IPv4 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_igmp_saddr_compare(Ipmcp_igmp_saddr_t *obj , struct Ip_in_addr *saddr)
|
|
{
|
|
return obj->saddr.s_addr == saddr->s_addr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_obj_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for the multicast address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_igmp_maddr_obj_hash(Ipmcp_igmp_maddr_t *obj)
|
|
{
|
|
return ipcom_hash_update(&obj->maddr, sizeof(obj->maddr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_compare
|
|
*===========================================================================
|
|
* Description: Compares a MCP multicast address entry with a IPv4 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_igmp_maddr_compare(Ipmcp_igmp_maddr_t *obj , struct Ip_in_addr *maddr)
|
|
{
|
|
return obj->maddr.s_addr == maddr->s_addr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_upstream_maddr_obj_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for a upstream multicast address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_igmp_upstream_maddr_obj_hash(Ipmcp_igmp_upstream_maddr_t *obj)
|
|
{
|
|
return ipcom_hash_update(&obj->maddr, sizeof(obj->maddr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_upstream_maddr_compare
|
|
*===========================================================================
|
|
* Description: Compares an upstream multicast address entry with a IPv4 address.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_igmp_upstream_maddr_compare(Ipmcp_igmp_upstream_maddr_t *obj , struct Ip_in_addr *maddr)
|
|
{
|
|
return obj->maddr.s_addr == maddr->s_addr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_is_maddr_link_local
|
|
*===========================================================================
|
|
* Description: Returns IP_TRUE if the specified address fall inside the
|
|
* range 224.0.0.0 to 224.0.0.255.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_igmp_is_maddr_link_local(struct Ip_in_addr *maddr)
|
|
{
|
|
Ip_u8 *a = (Ip_u8 *) maddr;
|
|
return a[0] == 224 && a[1] == 0 && a[2] == 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_get_primary_addr
|
|
*===========================================================================
|
|
* Description: Gets the primary IPv4 address of the specified interface.
|
|
* Parameters: ifindex - The index of the interface for which the primary
|
|
* address should be returned.
|
|
* primary_addr - Will hold the primary address if the call
|
|
* succeeds.
|
|
* Returns: 0 = success
|
|
* <0 = error
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmcp_igmp_get_primary_addr(Ip_u32 ifindex,
|
|
struct Ip_in_addr *primary_addr)
|
|
{
|
|
struct Ip_ifreq ifreq;
|
|
|
|
if (ipcom_if_indextoname(ifindex, ifreq.ifr_name) == IP_NULL)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_igmp_get_primary_addr:"
|
|
" Could not get a name for interface with index %d", ifindex);
|
|
return -1;
|
|
}
|
|
#ifdef IP_PORT_LAS
|
|
if (ipcom_socketioctl(ipmcp.cfg_fd, IP_SIOCGIFADDR, &ifreq) < 0)
|
|
#else
|
|
if (ipcom_socketioctl(ipmcp.ipd_fd, IP_SIOCGIFADDR, &ifreq) < 0)
|
|
#endif
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_socketioctl IP_SIOCGIFADDR: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
return -1;
|
|
}
|
|
|
|
ipcom_memcpy(primary_addr,
|
|
&((struct Ip_sockaddr_in *)&ifreq.ip_ifr_addr)->sin_addr,
|
|
sizeof(*primary_addr));
|
|
|
|
if (primary_addr->s_addr == IP_INADDR_ANY)
|
|
{
|
|
IPCOM_LOG1(NOTICE, "ipmcp_igmp_get_primary_addr:"
|
|
" No primary address set on interface %d", ifindex);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_upstream_maddr_get_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_igmp_upstream_maddr_get_addr(Ipmcp_igmp_upstream_maddr_t *maddr)
|
|
{
|
|
return &maddr->maddr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_upstream_maddr_change_mode(Ipmcp_igmp_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_igmp_set_sock_addr(&req.gr_group, &maddr->maddr);
|
|
(void) ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
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.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_MCAST_JOIN_GROUP,
|
|
&req, sizeof(req)) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipmcp_igmp_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_igmp_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_igmp_upstream_maddr_add_source(Ipmcp_igmp_upstream_maddr_t *maddr, struct Ip_in_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_igmp_set_sock_addr(&sreq.gsr_group, &maddr->maddr);
|
|
ipmcp_igmp_set_sock_addr(&sreq.gsr_source, src);
|
|
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
(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_igmp_upstream_maddr_add_source: setsockopt failed on %s: %s",
|
|
ipmcp.upstream_if.ifname, ipcom_strerror(ipcom_errno));
|
|
IP_PANIC2();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_upstream_maddr_del_source(Ipmcp_igmp_upstream_maddr_t *maddr, struct Ip_in_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_igmp_set_sock_addr(&sreq.gsr_group, &maddr->maddr);
|
|
ipmcp_igmp_set_sock_addr(&sreq.gsr_source, src);
|
|
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
(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_igmp_upstream_maddr_del_source: setsockopt failed on %s: %s",
|
|
ipmcp.upstream_if.ifname, ipcom_strerror(ipcom_errno));
|
|
IP_PANIC2();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_upstream_maddr_flush_mroutes(Ipmcp_igmp_upstream_maddr_t *maddr_obj)
|
|
{
|
|
struct Ip_mfcctl mfctl;
|
|
int i;
|
|
|
|
ipcom_memset(&mfctl, 0, sizeof(mfctl));
|
|
mfctl.mfcc_mcastgrp = maddr_obj->maddr;
|
|
mfctl.mfcc_parent = ipmcp.upstream_if.igmp.vif_index;
|
|
|
|
for (i = 0; i < IPMCP_IGMP_MAX_SRC_ADDRS; i++)
|
|
{
|
|
if (maddr_obj->saddrs[i].s_addr == IP_INADDR_ANY)
|
|
continue;
|
|
|
|
mfctl.mfcc_origin = maddr_obj->saddrs[i];
|
|
|
|
IPCOM_LOG2(DEBUG, "IGMP: deleting mcast route from %s to group %s",
|
|
ipcom_inet_ntop(IP_AF_INET, &mfctl.mfcc_origin, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET, &mfctl.mfcc_mcastgrp, ipmcp.log_buf + IP_INET_ADDRSTRLEN, sizeof(ipmcp.log_buf)));
|
|
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_MRT_DEL_MFC,
|
|
&mfctl,
|
|
sizeof(mfctl)) < 0)
|
|
{
|
|
IPCOM_LOG4(ERR, "ipmcp_igmp_upstream_maddr_flush_mroutes: "
|
|
"setsockopt IPPROTO_IP, MRT_DEL_MFC failed on %s [%s %s]: %s",
|
|
ipmcp.upstream_if.ifname,
|
|
ipcom_inet_ntop(IP_AF_INET, &mfctl.mfcc_mcastgrp,
|
|
ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET, &mfctl.mfcc_mcastgrp,
|
|
ipmcp.log_buf + IP_INET_ADDRSTRLEN, sizeof(ipmcp.log_buf)),
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
maddr_obj->saddrs[i].s_addr = IP_INADDR_ANY;
|
|
}
|
|
maddr_obj->next_src_addr = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_flush_all_mroutes_cb
|
|
*===========================================================================
|
|
* Description: Adapter function for ipmcp_igmp_upstream_maddr_flush_mroutes()
|
|
* Parameters: upstream_maddr - An upstream multicast address.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_flush_all_mroutes_cb(Ipmcp_igmp_upstream_maddr_t *upstream_maddr,
|
|
void *unused)
|
|
{
|
|
(void) unused;
|
|
ipmcp_igmp_upstream_maddr_flush_mroutes(upstream_maddr);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_flush_all_mroutes
|
|
*===========================================================================
|
|
* Description: Flushes all IPv4 multicast routes.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_flush_all_mroutes(void)
|
|
{
|
|
ipcom_hash_for_each(ipmcp.upstream_if.igmp.maddr_head,
|
|
(Ipcom_hash_foreach_cb_func)ipmcp_igmp_flush_all_mroutes_cb,
|
|
IP_NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_upstream_maddr_t *
|
|
ipmcp_igmp_upstream_maddr_get(struct Ip_in_addr *maddr)
|
|
{
|
|
Ipmcp_igmp_upstream_maddr_t *maddr_obj;
|
|
|
|
maddr_obj = ipcom_hash_get(ipmcp.upstream_if.igmp.maddr_head, maddr);
|
|
if (maddr_obj == IP_NULL)
|
|
{
|
|
maddr_obj = ipcom_calloc(1, sizeof(Ipmcp_igmp_upstream_maddr_t));
|
|
if (maddr_obj == IP_NULL)
|
|
goto out_of_memory;
|
|
|
|
maddr_obj->base.filter = ipcom_set_new(sizeof(struct Ip_in_addr));
|
|
if (maddr_obj->base.filter == IP_NULL)
|
|
goto out_of_memory;
|
|
|
|
maddr_obj->maddr = *maddr;
|
|
maddr_obj->base.ops = &ipmcp.igmp.upstream_maddr_ops;
|
|
|
|
if (ipcom_hash_add(ipmcp.upstream_if.igmp.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_igmp_upstream_maddr_get: Failed to allocate memory");
|
|
IP_PANIC2();
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_upstream_maddr_delete
|
|
*===========================================================================
|
|
* Description: Deletes a multicast address from the upstream interface.
|
|
* Parameters: maddr_obj - The address to delete from the upstream interface.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_upstream_maddr_delete(Ipmcp_igmp_upstream_maddr_t *maddr_obj)
|
|
{
|
|
struct Ip_group_req req;
|
|
|
|
if (ipcom_hash_remove(ipmcp.upstream_if.igmp.maddr_head, maddr_obj) != IPCOM_SUCCESS)
|
|
IP_PANIC();
|
|
|
|
req.gr_interface = ipmcp.upstream_if.ifindex;
|
|
ipmcp_igmp_set_sock_addr(&req.gr_group, &maddr_obj->maddr);
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_MCAST_LEAVE_GROUP,
|
|
&req, sizeof(req)) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipmcp_igmp_upstream_maddr_delete: "
|
|
"setsockopt IPPROTO_IP, IP_MCAST_LEAVE_GROUP failed on %s: %s",
|
|
ipmcp.upstream_if.ifname, ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
ipmcp_igmp_upstream_maddr_flush_mroutes(maddr_obj);
|
|
ipcom_set_delete(maddr_obj->base.filter);
|
|
ipcom_free(maddr_obj);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_upstream_maddr_delete_cb(Ipmcp_igmp_upstream_maddr_t *maddr_obj, void *unused)
|
|
{
|
|
(void) unused;
|
|
ipmcp_igmp_upstream_maddr_delete(maddr_obj);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_store_mroute(struct Ip_in_addr *maddr, struct Ip_in_addr *src)
|
|
{
|
|
Ipmcp_igmp_upstream_maddr_t *maddr_obj;
|
|
|
|
maddr_obj = ipmcp_igmp_upstream_maddr_get(maddr);
|
|
if (maddr_obj == IP_NULL)
|
|
return IP_FALSE;
|
|
|
|
if (maddr_obj->saddrs[maddr_obj->next_src_addr].s_addr != IP_INADDR_ANY)
|
|
{
|
|
/* Ring buffer overflow, remove the oldest entry */
|
|
struct Ip_mfcctl mfctl;
|
|
|
|
IPCOM_LOG2(DEBUG, "IGMP: deleting mcast route from %s to group %s",
|
|
ipcom_inet_ntop(IP_AF_INET, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET, maddr, ipmcp.log_buf + IP_INET_ADDRSTRLEN, sizeof(ipmcp.log_buf)));
|
|
|
|
ipcom_memset(&mfctl, 0, sizeof(mfctl));
|
|
mfctl.mfcc_mcastgrp = maddr_obj->maddr;
|
|
mfctl.mfcc_parent = ipmcp.upstream_if.igmp.vif_index;
|
|
mfctl.mfcc_origin = maddr_obj->saddrs[maddr_obj->next_src_addr];
|
|
(void)ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_MRT_DEL_MFC,
|
|
&mfctl,
|
|
sizeof(mfctl));
|
|
}
|
|
|
|
maddr_obj->saddrs[maddr_obj->next_src_addr] = *src;
|
|
maddr_obj->next_src_addr = (maddr_obj->next_src_addr + 1) % IPMCP_IGMP_MAX_SRC_ADDRS;
|
|
return IP_TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_netif_to_maddr_head
|
|
*===========================================================================
|
|
* Description: Returns the IPv4 multicast address object head.
|
|
* Parameters: netif - A downstream interface.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipcom_hash *
|
|
ipmcp_igmp_netif_to_maddr_head(Ipmcp_netif_t *netif)
|
|
{
|
|
return netif->igmp.maddr_head;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_update_upstream_if_filter(Ipmcp_igmp_maddr_t *maddr)
|
|
{
|
|
Ipmcp_igmp_upstream_maddr_t *maddr_obj;
|
|
|
|
maddr_obj = ipmcp_igmp_upstream_maddr_get(&maddr->maddr);
|
|
if (maddr_obj != IP_NULL)
|
|
{
|
|
/* Flushing the routes for each call to this function will result in correct
|
|
behavior using very simple logic, but it will lead to more IP_IGMPMSG_NOCACHE messages */
|
|
ipmcp_igmp_upstream_maddr_flush_mroutes(maddr_obj);
|
|
ipmcp_update_upstream_if_filter(&maddr_obj->base, ipmcp_igmp_netif_to_maddr_head);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_forward_stream(Ipmcp_netif_t *netif,
|
|
struct Ip_in_addr *maddr,
|
|
struct Ip_in_addr *saddr)
|
|
{
|
|
Ipmcp_maddr_t *maddr_obj;
|
|
Ipmcp_saddr_t *saddr_obj;
|
|
|
|
if (netif->igmp.state != IPMCP_LINK_STATE_QUERIER)
|
|
return IP_FALSE;
|
|
|
|
maddr_obj = ipcom_hash_get(netif->igmp.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_igmp_add_mroute
|
|
*===========================================================================
|
|
* Description: Adds a multicast route entry.
|
|
* Parameters: vifi - The virtual interface the packet was received at.
|
|
* maddr - The multicast address this route will target.
|
|
* src - The source of the multicast packets.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmcp_igmp_add_mroute(Ip_vifi_t vifi, struct Ip_in_addr *maddr, struct Ip_in_addr *src)
|
|
{
|
|
int n_vif = 0;
|
|
struct Ip_mfcctl mfctl;
|
|
Ipmcp_netif_t *netif;
|
|
Ip_bool add_mroute = IP_FALSE;
|
|
|
|
/* Setup multicast route */
|
|
ipcom_memset(&mfctl, 0, sizeof(mfctl));
|
|
mfctl.mfcc_parent = vifi;
|
|
mfctl.mfcc_mcastgrp = *maddr;
|
|
mfctl.mfcc_origin = *src;
|
|
|
|
if (vifi == ipmcp.upstream_if.igmp.vif_index)
|
|
add_mroute = IP_TRUE;
|
|
else
|
|
{
|
|
/* Must always forward to the upstream interface when
|
|
multicast traffic entrers at one of the downstream interfaces */
|
|
n_vif++;
|
|
mfctl.mfcc_ttls[ipmcp.upstream_if.igmp.vif_index] = 1;
|
|
}
|
|
|
|
/* Check MRT table on each interface */
|
|
for (netif = IPCOM_LIST_FIRST(&ipmcp.downstream_ifs_head);
|
|
netif != IP_NULL;
|
|
netif = IPCOM_LIST_NEXT(&netif->list))
|
|
{
|
|
if (vifi == netif->igmp.vif_index)
|
|
add_mroute = (netif->igmp.state == IPMCP_LINK_STATE_QUERIER);
|
|
else if (ipmcp_igmp_forward_stream(netif, maddr, src))
|
|
{
|
|
/* Found an interface to forward on */
|
|
n_vif++;
|
|
mfctl.mfcc_ttls[netif->igmp.vif_index] = 1;
|
|
}
|
|
}
|
|
|
|
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, "IGMP: adding mcast route from %s to group %s",
|
|
ipcom_inet_ntop(IP_AF_INET, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET, maddr, ipmcp.log_buf + IP_INET_ADDRSTRLEN, sizeof(ipmcp.log_buf)));
|
|
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_MRT_ADD_MFC,
|
|
&mfctl, sizeof(mfctl)) < 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipmcp_igmp_add_mroute: failed to add mroute: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
return -ipcom_errno;
|
|
}
|
|
ipmcp_igmp_store_mroute(maddr, src);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_add_saddr_to_query(struct Ip_in_addr *saddr, Ipmcp_igmp_query_pkt_t *query)
|
|
{
|
|
query->source_addresses[query->num_sources++] = *saddr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_query_send
|
|
*===========================================================================
|
|
* Description: Send IGMP 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_igmp_query_send(Ipmcp_netif_t *netif,
|
|
struct Ip_in_addr *maddr,
|
|
Ip_u32 max_response_interval_msec,
|
|
Ip_bool suppress_router_side_processing,
|
|
Ipcom_set *saddrs)
|
|
{
|
|
Ipmcp_igmp_query_pkt_t *pkt = (Ipmcp_igmp_query_pkt_t *) ipmcp.send_buf;
|
|
struct Ip_sockaddr_in to;
|
|
struct Ip_msghdr m;
|
|
struct Ip_iovec iov;
|
|
struct Ip_in_pktinfo *pktinfo;
|
|
Ip_u8 *has_router_alert;
|
|
struct Ip_cmsghdr *c;
|
|
Ip_u8 cmsg_buf[IP_CMSG_SPACE(sizeof(struct Ip_in_pktinfo))
|
|
+ IP_CMSG_SPACE(sizeof(Ip_u8))];
|
|
int version;
|
|
unsigned msg_len;
|
|
|
|
version = ipcom_sysvar_get_as_int("ipmcp.igmp.CompatibilityMode", 3);
|
|
|
|
ipcom_memset(&to, 0, sizeof(to));
|
|
IPCOM_SA_LEN_SET(&to, sizeof(to));
|
|
to.sin_family = IP_AF_INET;
|
|
to.sin_addr = *maddr;
|
|
|
|
ipcom_memset(pkt, 0, sizeof(*pkt));
|
|
pkt->type = IPMCP_IGMP_TYPE_MEMBERSHIP_QUERY;
|
|
|
|
if (version > 1)
|
|
pkt->max_resp_time = max_response_interval_msec / 100;
|
|
/*
|
|
* else: RFC1112 specifies this field as unused for IGMPv1
|
|
*/
|
|
|
|
if (maddr->s_addr != ipmcp.igmp.all_hosts_addr.s_addr)
|
|
pkt->group_address = *maddr;
|
|
/* else: general query */
|
|
|
|
switch (version)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
msg_len = 8;
|
|
break;
|
|
default:
|
|
msg_len = 12;
|
|
if (ipmcp.config.robustness_variable <= 7)
|
|
/* RFC3376 - 4.1.6.
|
|
If non-zero, the QRV field contains the [Robustness Variable] value
|
|
used by the querier, i.e., the sender of the Query. If the querier's
|
|
[Robustness Variable] exceeds 7, the maximum value of the QRV field,
|
|
the QRV 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_IGMPV3_QUERY_SUPRESS_BIT);
|
|
pkt->qqic = ipmcp_qqic_from_msec(ipmcp.config.query_interval, 3);
|
|
break;
|
|
}
|
|
|
|
if (saddrs != IP_NULL)
|
|
{
|
|
ipcom_set_for_each(saddrs,
|
|
(Ipcom_set_foreach_cb_func) ipmcp_igmp_add_saddr_to_query,
|
|
pkt);
|
|
msg_len += pkt->num_sources * 4;
|
|
pkt->num_sources = ip_htons(pkt->num_sources);
|
|
}
|
|
|
|
pkt->checksum = ipcom_in_checksum(pkt, msg_len);
|
|
|
|
iov.iov_base = pkt;
|
|
iov.iov_len = msg_len;
|
|
|
|
/*
|
|
* Specify egress interface
|
|
*/
|
|
ipcom_memset(cmsg_buf, 0, sizeof(cmsg_buf));
|
|
c = (struct Ip_cmsghdr *) cmsg_buf;
|
|
c->cmsg_len = IP_CMSG_LEN(sizeof(struct Ip_in_pktinfo));
|
|
c->cmsg_level = IP_IPPROTO_IP;
|
|
c->cmsg_type = IP_IP_PKTINFO;
|
|
pktinfo = IP_CMSG_DATA(c);
|
|
pktinfo->ipi_ifindex = netif->ifindex;
|
|
|
|
/*
|
|
* Include router alert IP option
|
|
*/
|
|
c = (struct Ip_cmsghdr *) ((char*)c + IP_CMSG_SPACE(sizeof(struct Ip_in_pktinfo)));
|
|
c->cmsg_len = IP_CMSG_LEN(sizeof(Ip_u8));
|
|
c->cmsg_level = IP_IPPROTO_IP;
|
|
c->cmsg_type = IP_IP_ROUTER_ALERT;
|
|
has_router_alert = IP_CMSG_DATA(c);
|
|
*has_router_alert = IP_TRUE;
|
|
|
|
m.msg_name = &to;
|
|
m.msg_namelen = sizeof(to);
|
|
m.msg_iov = &iov;
|
|
m.msg_iovlen = 1;
|
|
m.msg_control = cmsg_buf;
|
|
m.msg_controllen = sizeof(cmsg_buf);
|
|
m.msg_flags = 0;
|
|
|
|
if (ipcom_sendmsg(ipmcp.igmp.fd, &m, 0) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "IGMP: Failed to send message on interface %s: %s",
|
|
netif->ifname,
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_general_query_tmo(Ipmcp_netif_t *netif)
|
|
{
|
|
Ipmcp_igmp_netif_t *i = &netif->igmp;
|
|
Ip_bool restart_timer = IP_TRUE;
|
|
|
|
if (i->state == IPMCP_LINK_STATE_INACTIVE)
|
|
/* Retry later (this is probably because of no unicast address) */
|
|
return IP_TRUE;
|
|
|
|
ipmcp_igmp_query_send(netif,
|
|
&ipmcp.igmp.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_igmp_general_query_tmo,
|
|
netif,
|
|
duration);
|
|
restart_timer = IP_FALSE;
|
|
}
|
|
|
|
return restart_timer;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_resume_querier_tmo
|
|
*===========================================================================
|
|
* Description: Timeout routine called when resuming to querier state.
|
|
* Parameters: netif - The link to resume as querier.
|
|
* Returns: IP_FALSE - Do not reschedule this timeout.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_igmp_resume_querier_tmo(Ipmcp_netif_t *netif)
|
|
{
|
|
IPCOM_LOG1(NOTICE, "IGMP: 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 IGMP Querier.
|
|
...
|
|
*/
|
|
ipmcp_igmp_flush_all_mroutes();
|
|
netif->igmp.state = IPMCP_LINK_STATE_QUERIER;
|
|
netif->igmp.robustness_variable = ipmcp.config.robustness_variable;
|
|
netif->igmp.query_interval = ipmcp.config.query_interval;
|
|
netif->igmp.startup_query_count = ipmcp.config.startup_query_count;
|
|
ipmcp_igmp_general_query_tmo(netif);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_version_1_or_2_compat_tmo
|
|
*===========================================================================
|
|
* Description: Called when IGMPv1 or IGMPv2 compatibilty mode can be exited
|
|
* Parameters: maddr - A multicast address.
|
|
* Returns: IP_FALSE - The timeout should not be rescheduled.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_igmp_version_1_or_2_compat_tmo(Ipmcp_igmp_maddr_t *maddr)
|
|
{
|
|
(void) maddr;
|
|
IPCOM_LOG2(INFO,
|
|
"IGMP: Exit IGMP%s compatibility mode on %s",
|
|
IPCOM_TMO2_PENDING(&maddr->v1_host_present_timer) ? "v2, but still in IGMPv1"
|
|
: (IPCOM_TMO2_PENDING(&maddr->v2_host_present_timer) ? "v1, but still in IGMPv2"
|
|
: ""),
|
|
maddr->base.netif->ifname);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_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_igmp_specific_query_tmo(Ipmcp_igmp_maddr_t *maddr)
|
|
{
|
|
(void) maddr;
|
|
IPCOM_LOG1(DEBUG,
|
|
"IGMP: Specific query finished on %s since max response period passed",
|
|
maddr->base.netif->ifname);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_other_querier_present_timeout
|
|
*===========================================================================
|
|
* Description: Returns the other querier present timeout in msec.
|
|
* Parameters: i - IGMP specific interface config
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u32
|
|
ipmcp_igmp_other_querier_present_timeout(Ipmcp_igmp_netif_t *i)
|
|
{
|
|
/*
|
|
* RFC 3376
|
|
*
|
|
* 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_igmp_handle_query
|
|
*===========================================================================
|
|
* Description: Handles general or specific query from another multicast router.
|
|
* 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_igmp_handle_query(Ipmcp_netif_t *netif,
|
|
Ipmcp_igmp_query_pkt_t *query,
|
|
struct Ip_in_addr *src,
|
|
int version)
|
|
{
|
|
Ipmcp_igmp_netif_t *i = &netif->igmp;
|
|
Ipmcp_igmp_maddr_t *maddr_obj;
|
|
|
|
IPCOM_LOG4(DEBUG, "IGMPv%d membership query received from %s group %s on %s",
|
|
version,
|
|
ipcom_inet_ntop(IP_AF_INET, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET, &query->group_address,
|
|
ipmcp.log_buf + IP_INET_ADDRSTRLEN,
|
|
sizeof(ipmcp.log_buf) - IP_INET_ADDRSTRLEN),
|
|
netif->ifname);
|
|
|
|
maddr_obj = ipcom_hash_get(netif->igmp.maddr_head, &query->group_address);
|
|
|
|
/*
|
|
* RFC 2236 states that this node should only consider query
|
|
* messages from routers with a numerically lower IP-address.
|
|
*/
|
|
if (ip_ntohl(i->primary_address.s_addr) <= ip_ntohl(src->s_addr))
|
|
/*
|
|
* This node has a numerically lower address.
|
|
*/
|
|
return;
|
|
|
|
if (i->state != IPMCP_LINK_STATE_NON_QUERIER)
|
|
{
|
|
/*
|
|
* else: RFC 3376 - When a router receives a query with a
|
|
* lower IP address, it sets the Other-Querier-Present timer
|
|
* to Other Querier Present Interval and ceases to send
|
|
* queries on the network if it was the previously elected
|
|
* 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;
|
|
|
|
/* Enter non-querier state */
|
|
IPCOM_LOG1(NOTICE, "IGMP: 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 IGMP Querier.
|
|
...
|
|
*/
|
|
ipmcp_igmp_flush_all_mroutes();
|
|
}
|
|
|
|
if (version == 3)
|
|
{
|
|
/* RFC3376 - 4.1.6
|
|
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 the
|
|
receivers use the default [Robustness Variable] value specified in
|
|
section 8.1 or a statically configured value.
|
|
*/
|
|
if (IPMCP_IGMPV3_GET_QRV(query))
|
|
netif->igmp.robustness_variable = IPMCP_IGMPV3_GET_QRV(query);
|
|
else
|
|
netif->igmp.robustness_variable = ipmcp.config.robustness_variable;
|
|
|
|
/* RFC3376 - 4.1.7
|
|
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 8.2.
|
|
*/
|
|
if (query->qqic)
|
|
netif->igmp.query_interval = ipmcp_qqic_to_msec(query->qqic);
|
|
else
|
|
netif->igmp.query_interval = ipmcp.config.query_interval;
|
|
|
|
if (IP_BIT_ISFALSE(query->res_s_qrv, IPMCP_IGMPV3_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_igmp_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_igmp_resume_querier_tmo,
|
|
netif,
|
|
ipmcp_igmp_other_querier_present_timeout(i));
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_handle_membership_report_rec
|
|
*===========================================================================
|
|
* Description: Handles v3 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 IGMPv3 record.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_handle_membership_report_rec(Ipmcp_netif_t *netif,
|
|
Ipmcp_igmp_group_rec_pkt_t *rec,
|
|
int version)
|
|
{
|
|
Ipmcp_igmp_maddr_t *maddr_obj;
|
|
Ip_bool new_entry = IP_FALSE;
|
|
Ipcom_tmo2 *compat_tmo = IP_NULL;
|
|
Ipmcp_igmp_rec_t rec_obj;
|
|
|
|
if (IP_IN_CLASSD(rec->group_address.s_addr) == IP_FALSE
|
|
|| ipmcp_igmp_is_maddr_link_local(&rec->group_address))
|
|
/* Never forward link local multicast addresses */
|
|
return;
|
|
|
|
maddr_obj = ipcom_hash_get(netif->igmp.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_igmp_handle_membership_report_rec: ipcom_calloc failed");
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
maddr_obj->base.include_set =
|
|
ipcom_hash_new((Ipcom_hash_obj_func)ipmcp_igmp_saddr_obj_hash,
|
|
(Ipcom_hash_key_func)ipmcp_igmp_addr_key_hash,
|
|
(Ipcom_hash_cmp_func)ipmcp_igmp_saddr_compare);
|
|
maddr_obj->base.exclude_set =
|
|
ipcom_hash_new((Ipcom_hash_obj_func)ipmcp_igmp_saddr_obj_hash,
|
|
(Ipcom_hash_key_func)ipmcp_igmp_addr_key_hash,
|
|
(Ipcom_hash_cmp_func)ipmcp_igmp_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_igmp_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.igmp.maddr_ops;
|
|
maddr_obj->base.domain = IP_AF_INET;
|
|
maddr_obj->base.addr_len = sizeof(struct Ip_in_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->v2_host_present_timer);
|
|
IPCOM_TMO2_INIT(&maddr_obj->specific_query_pending_timer);
|
|
|
|
new_entry = IP_TRUE;
|
|
}
|
|
|
|
if (version == 1 || version == 2)
|
|
{
|
|
/* IGMPv1 or IPGMPv2 host report, start compatibility timer */
|
|
if (version == 1)
|
|
compat_tmo = &maddr_obj->v1_host_present_timer;
|
|
else
|
|
compat_tmo = &maddr_obj->v2_host_present_timer;
|
|
|
|
if (IPCOM_TMO2_PENDING(compat_tmo))
|
|
IPCOM_LOG2(INFO, "IGMP: Enter IGMPv%d compatibility mode on %s", version, netif->ifname);
|
|
(void)ipcom_tmo2_cancel(compat_tmo);
|
|
(void)ipcom_tmo2_request(ipmcp.tmo_head,
|
|
compat_tmo,
|
|
(Ipcom_tmo2_func)ipmcp_igmp_version_1_or_2_compat_tmo,
|
|
maddr_obj,
|
|
netif->igmp.robustness_variable
|
|
* netif->igmp.query_interval
|
|
+ ipmcp.config.query_response_interval);
|
|
}
|
|
|
|
rec_obj.base.ops = &ipmcp.igmp.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->igmp.maddr_head, maddr_obj) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_free(maddr_obj);
|
|
IPCOM_LOG0(ERR, "ipmcp_igmp_handle_membership_report_rec: ipcom_hash_addr failed");
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
IPCOM_LOG2(DEBUG, "IGMP: Created entry for %s on %s",
|
|
ipcom_inet_ntop(IP_AF_INET, &maddr_obj->maddr, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
netif->ifname);
|
|
}
|
|
|
|
ipmcp_igmp_update_upstream_if_filter(maddr_obj);
|
|
return;
|
|
|
|
cleanup:
|
|
if (new_entry)
|
|
{
|
|
if (compat_tmo != IP_NULL)
|
|
(void)ipcom_tmo2_cancel(compat_tmo);
|
|
ipcom_free(maddr_obj);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_handle_membership_report
|
|
*===========================================================================
|
|
* Description: Handles v1 and v2 membership report for an address.
|
|
* Parameters: netif - The interface the query was received at.
|
|
* report - The membership report message.
|
|
* version - IGMP protocol version for the report (1 or 2).
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_handle_membership_report(Ipmcp_netif_t *netif,
|
|
Ipmcp_igmp_query_pkt_t *report,
|
|
int version)
|
|
{
|
|
Ipmcp_igmp_group_rec_pkt_t rec;
|
|
|
|
ip_assert(version == 1 || version == 2);
|
|
|
|
/* RFC3376 - 7.3.2: Membership 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_igmp_handle_membership_report_rec(netif, &rec, version);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_handle_leave_group_v2
|
|
*===========================================================================
|
|
* Description: Handles membership report for an address.
|
|
* Parameters: netif - The interface the query was received at.
|
|
* report - The leave group message.
|
|
* Returns: 0 = success,
|
|
* <0 = error.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_handle_leave_group_v2(Ipmcp_netif_t *netif, Ipmcp_igmp_query_pkt_t *report)
|
|
{
|
|
Ipmcp_igmp_group_rec_pkt_t rec;
|
|
|
|
/* RFC3376 - 7.3.2: Leave group 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_igmp_handle_membership_report_rec(netif, &rec, 2);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_mroute_input
|
|
*===========================================================================
|
|
* Description: Handles multicast routing events sent from the stack.
|
|
* Parameters: m - The message.
|
|
* Returns: 0 = success,
|
|
* <0 = error.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmcp_igmp_mroute_input(struct Ip_igmpmsg *m)
|
|
{
|
|
IPCOM_LOG2(DEBUG2, "IGMP: Got mroute message %s --> %s",
|
|
ipcom_inet_ntop(IP_AF_INET, &m->im_src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET, &m->im_dst, ipmcp.log_buf + IP_INET_ADDRSTRLEN, sizeof(ipmcp.log_buf)));
|
|
if (ipmcp.state != IPMCP_STATE_RUNNING)
|
|
/* Uplink interface not active */
|
|
return 0;
|
|
|
|
return ipmcp_igmp_add_mroute(m->im_vif, &m->im_dst, &m->im_src);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_is_msg_ok
|
|
*===========================================================================
|
|
* Description: Basic sanity test on a received IGMP packet.
|
|
* Parameters: version - IGMP version the packet is using.
|
|
* tos - type-of-service on the packet
|
|
* has_ra - IP_TRUE if the packet included a router
|
|
* advertisment IP-option
|
|
* ucast_dst - IP_TRUE if the packet was sent to a
|
|
* unicast address
|
|
* netif - interface the packet was received on
|
|
* src - address of the sender.
|
|
* Returns: IP_TRUE if the packet passes basic sanity, IP_FALSE
|
|
* otherwise.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmcp_igmp_is_msg_ok(int version,
|
|
Ip_u8 tos,
|
|
Ip_bool has_ra,
|
|
Ip_bool ucast_dst,
|
|
const Ipmcp_netif_t *netif,
|
|
const struct Ip_in_addr *src)
|
|
{
|
|
/* Later v2 specs require RA present in report */
|
|
if (has_ra == IP_FALSE)
|
|
{
|
|
IPCOM_LOG3(NOTICE,
|
|
"IGMPv%d membership report received from %s on %s missing Router Alert option",
|
|
version,
|
|
ipcom_inet_ntop(IP_AF_INET, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
netif->ifname);
|
|
/* Ignore this packet */
|
|
return IP_FALSE;
|
|
}
|
|
|
|
if (version == 3)
|
|
{
|
|
/*
|
|
* RFC 3376 page 7:
|
|
* 4. Message Formats
|
|
*
|
|
* IGMP messages are encapsulated in IPv4 datagrams, with an IP
|
|
* protocol number of 2. Every IGMP message described in this
|
|
* document is sent with an IP Time-to-Live of 1, IP Precedence of
|
|
* Internetwork Control (e.g., Type of Service 0xc0), and carries
|
|
* an IP Router Alert option [RFC-2113] in its IP header.
|
|
*/
|
|
if (tos != 0xc0)
|
|
{
|
|
IPCOM_LOG3(NOTICE,
|
|
"IGMPv3 membership report received from %s on %s with TOS=%#x, should be 0xc0",
|
|
ipcom_inet_ntop(IP_AF_INET, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
netif->ifname,
|
|
tos);
|
|
/* Ignore this packet */
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/*
|
|
* IGMPv1/v2 allows for unicast reports, IGMPv3 does not.
|
|
*/
|
|
if (ucast_dst)
|
|
{
|
|
IPCOM_LOG2(NOTICE,
|
|
"IGMPv3 membership report received from %s on %s with a unicast destination",
|
|
ipcom_inet_ntop(IP_AF_INET, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
netif->ifname);
|
|
/* Ignore this packet */
|
|
return IP_FALSE;
|
|
}
|
|
}
|
|
|
|
return version <= ipcom_sysvar_get_as_int("ipmcp.igmp.CompatibilityMode", 3);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_igmp_input
|
|
*===========================================================================
|
|
* Description: Handles multicast routing events sent from the stack.
|
|
* Parameters: netif - The interface the message was received at.
|
|
* igmp - The IGMP message
|
|
* igmp_len - The length of the IGMP message.
|
|
* src - The sender of the message.
|
|
* tos - value of the type-of-service field in the message
|
|
* router_alert - IP_TRUE if the received message
|
|
* contained a router alert option.
|
|
* ucast_dst - IP_TRUE if the packet was sent to a
|
|
* unicast destination.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_igmp_input(Ipmcp_netif_t *netif,
|
|
void *igmp,
|
|
int igmp_len,
|
|
struct Ip_in_addr *src,
|
|
Ip_u8 tos,
|
|
Ip_bool router_alert,
|
|
Ip_bool ucast_dst)
|
|
{
|
|
int v, num_rec;
|
|
Ipmcp_igmp_report_pkt_t *report = igmp;
|
|
Ipmcp_igmp_query_pkt_t *query = igmp;
|
|
Ipmcp_igmp_group_rec_pkt_t *next_rec;
|
|
|
|
switch (report->type)
|
|
{
|
|
case IPMCP_IGMP_TYPE_MEMBERSHIP_QUERY:
|
|
if (igmp_len == IPMCP_IGMP_MIN_PKT_LEN)
|
|
v = (query->max_resp_time ? 2 : 1);
|
|
else
|
|
{
|
|
if (igmp_len != 12 + ip_htons(query->num_sources) * 4)
|
|
/* Invalid query, ignore this message */
|
|
return;
|
|
v = 3;
|
|
}
|
|
if (ipmcp_igmp_is_msg_ok(v, tos, router_alert, ucast_dst, netif, src))
|
|
ipmcp_igmp_handle_query(netif, query, src, v);
|
|
break;
|
|
|
|
case IPMCP_IGMP_TYPE_MEMBERSHIP_REPORT:
|
|
ipmcp_igmp_handle_membership_report(netif, igmp, 1);
|
|
break;
|
|
|
|
case IPMCP_IGMP_TYPE_MEMBERSHIP_REPORT_V2:
|
|
if (ipmcp_igmp_is_msg_ok(2, tos, router_alert, ucast_dst, netif, src))
|
|
ipmcp_igmp_handle_membership_report(netif, igmp, 2);
|
|
break;
|
|
|
|
case IPMCP_IGMP_TYPE_LEAVE_GROUP_V2:
|
|
if(ucast_dst == IP_TRUE)
|
|
return ;/*RFC2236 P9 */
|
|
if (ipmcp_igmp_is_msg_ok(2, tos, router_alert, ucast_dst, netif, src))
|
|
ipmcp_igmp_handle_leave_group_v2(netif, igmp);
|
|
break;
|
|
|
|
case IPMCP_IGMP_TYPE_MEMBERSHIP_REPORT_V3:
|
|
if (ipmcp_igmp_is_msg_ok(3, tos, router_alert, ucast_dst, netif, src) == IP_FALSE)
|
|
return;
|
|
|
|
/* Size of mandatory message part */
|
|
igmp_len -= 8;
|
|
num_rec = ip_ntohs(report->num_group_rec);
|
|
next_rec = report->recs;
|
|
for (v = 0; v < num_rec; v++)
|
|
{
|
|
Ipmcp_igmp_group_rec_pkt_t *rec = next_rec;
|
|
int rec_len;
|
|
|
|
rec_len = 8 + (ip_ntohs(rec->num_sources) + rec->aux_data_len) * 4;
|
|
if (igmp_len < rec_len)
|
|
{
|
|
IPCOM_LOG2(NOTICE,
|
|
"IGMPv3 invalid membership report received from %s on %s",
|
|
ipcom_inet_ntop(IP_AF_INET, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
netif->ifname);
|
|
/* Ignore this packet */
|
|
return;
|
|
}
|
|
igmp_len -= rec_len;
|
|
next_rec = (Ipmcp_igmp_group_rec_pkt_t *) ((Ip_u8 *) rec + rec_len);
|
|
|
|
ipmcp_igmp_handle_membership_report_rec(netif, rec, 3);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
IPCOM_LOG3(NOTICE, "IGMP unknown message type (%d) received from %s on %s",
|
|
report->type,
|
|
ipcom_inet_ntop(IP_AF_INET, src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
netif->ifname);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_activate
|
|
*===========================================================================
|
|
* Description: Activates router activities on this interface.
|
|
* Parameters: netif - The interface we should start acting as a multicast router.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_activate(Ipmcp_netif_t *netif)
|
|
{
|
|
struct Ip_ip_mreq mreq;
|
|
|
|
if (netif->igmp.need_if_create
|
|
|| netif->igmp.primary_address.s_addr == IP_INADDR_ANY)
|
|
/* Stay in inactive mode until a primary address is set */
|
|
return;
|
|
|
|
/* Enter promiscuous mode on this interface */
|
|
ipcom_memset(&mreq, 0, sizeof(mreq));
|
|
mreq.imr_interface = netif->igmp.primary_address;
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_IP_JOIN_GROUP,
|
|
&mreq, sizeof(mreq)) < 0)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipmcp_igmp_activate: setsockopt IPPROTO_IP, IP_JOIN_GROUP failed on %s: %s",
|
|
netif->ifname, ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
/* Start sending general queries */
|
|
(void)ipmcp_igmp_resume_querier_tmo(netif);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_deactivate
|
|
*===========================================================================
|
|
* Description: Dectivate IGMP server on specified interface.
|
|
* Parameters: .
|
|
* Returns: .
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_deactivate(Ipmcp_netif_t *netif)
|
|
{
|
|
struct Ip_ip_mreq mreq;
|
|
Ipmcp_igmp_netif_t *i = &netif->igmp;
|
|
|
|
/* Cancel downstream interface timer */
|
|
(void)ipcom_tmo2_cancel(&i->timer);
|
|
|
|
if (i->maddr_head != IP_NULL)
|
|
ipcom_hash_for_each(i->maddr_head,
|
|
(Ipcom_hash_foreach_cb_func)ipmcp_igmp_maddr_drop,
|
|
netif);
|
|
|
|
if (netif->igmp.state != IPMCP_LINK_STATE_INACTIVE
|
|
&& netif->igmp.primary_address.s_addr != IP_INADDR_ANY)
|
|
{
|
|
/* Leave promiscuous mode on this interface */
|
|
ipcom_memset(&mreq, 0, sizeof(mreq));
|
|
mreq.imr_interface = netif->igmp.primary_address;
|
|
(void)ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_IP_LEAVE_GROUP,
|
|
&mreq, sizeof(mreq));
|
|
}
|
|
|
|
/* Change state */
|
|
IPCOM_LOG1(NOTICE, "IGMP: Link state INACTIVE on %s", netif->ifname);
|
|
netif->igmp.state = IPMCP_LINK_STATE_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ########### Ipmcp_igmp_rec_t methods ########### */
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_rec_get_type
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u8
|
|
ipmcp_igmp_rec_get_type(Ipmcp_igmp_rec_t *self)
|
|
{
|
|
return self->rec->type;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_rec_get_maddr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_igmp_rec_get_maddr(Ipmcp_igmp_rec_t *self)
|
|
{
|
|
return &self->rec->group_address;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_rec_get_num_saddrs
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipmcp_igmp_rec_get_num_saddrs(Ipmcp_igmp_rec_t *self)
|
|
{
|
|
return ip_ntohs(self->rec->num_sources);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_rec_get_saddr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_igmp_rec_get_saddr(Ipmcp_igmp_rec_t *self, unsigned i)
|
|
{
|
|
ip_assert(ipmcp_igmp_rec_get_num_saddrs(self) > i);
|
|
return &self->rec->source_addresses[i];
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_rec_as_set
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipcom_set *
|
|
ipmcp_igmp_rec_as_set(Ipmcp_igmp_rec_t *self)
|
|
{
|
|
Ipcom_set *set;
|
|
unsigned s;
|
|
unsigned num = ip_ntohs(self->rec->num_sources);
|
|
|
|
set = ipcom_set_new(sizeof(struct Ip_in_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_igmp_rec_clear_source_list
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_rec_clear_source_list(Ipmcp_igmp_rec_t *self)
|
|
{
|
|
self->rec->num_sources = 0;
|
|
}
|
|
|
|
|
|
|
|
/* ########### Ipmcp_igmp_saddr_t methods ########### */
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_saddr_get_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_igmp_saddr_get_addr(Ipmcp_igmp_saddr_t *self)
|
|
{
|
|
return &self->saddr;
|
|
}
|
|
|
|
|
|
|
|
/* ########### Ipmcp_igmp_maddr_t methods ########### */
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_delete
|
|
*===========================================================================
|
|
* Description: Drops a multicast address from a specified interface.
|
|
* Parameters: maddr_obj - The address to destroy.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_maddr_delete(Ipmcp_igmp_maddr_t *maddr_obj)
|
|
{
|
|
Ipmcp_netif_t *netif = maddr_obj->base.netif;
|
|
|
|
/* Remove from hash table */
|
|
(void)ipcom_hash_remove(netif->igmp.maddr_head, maddr_obj);
|
|
ipmcp_igmp_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);
|
|
(void)ipcom_tmo2_cancel(&maddr_obj->v2_host_present_timer);
|
|
|
|
ipmcp_maddr_cleanup(&maddr_obj->base);
|
|
|
|
/* Free object */
|
|
ipcom_free(maddr_obj);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_drop
|
|
*===========================================================================
|
|
* Description: Drops a multicast address from a specified interface.
|
|
* Parameters: maddr_obj - The address to destroy.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_maddr_drop(Ipmcp_igmp_maddr_t *maddr_obj, void *unused)
|
|
{
|
|
(void) unused;
|
|
ipmcp_igmp_maddr_delete(maddr_obj);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_get_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipmcp_igmp_maddr_get_addr(Ipmcp_igmp_maddr_t *self)
|
|
{
|
|
return &self->maddr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_compatibility_mode
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmcp_igmp_maddr_compatibility_mode(Ipmcp_igmp_maddr_t *self)
|
|
{
|
|
if (IPCOM_TMO2_PENDING(&self->v1_host_present_timer))
|
|
return IPMCP_MCAST_COMPAT_IGMPV1;
|
|
if (IPCOM_TMO2_PENDING(&self->v2_host_present_timer))
|
|
return IPMCP_MCAST_COMPAT_IGMPV2_MLDV1;
|
|
return IPMCP_MCAST_COMPAT_IGMPV3_MLDV2;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_add_saddr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipmcp_igmp_saddr_t *
|
|
ipmcp_igmp_maddr_add_saddr(Ipmcp_maddr_t *self,
|
|
struct Ip_in_addr *saddr,
|
|
Ip_bool is_include_set)
|
|
{
|
|
Ipmcp_igmp_saddr_t *saddr_obj;
|
|
|
|
saddr_obj = ipcom_calloc(1, sizeof(*saddr_obj));
|
|
if(saddr_obj == IP_NULL)
|
|
{
|
|
return IP_NULL;
|
|
}
|
|
saddr_obj->base.ops = &ipmcp.igmp.saddr_ops;
|
|
saddr_obj->saddr = *saddr;
|
|
saddr_obj->base.maddr = self;
|
|
IPCOM_TMO2_INIT(&saddr_obj->base.timer);
|
|
|
|
if (ipcom_hash_add(is_include_set ? self->include_set : self->exclude_set,
|
|
saddr_obj) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_free(saddr_obj);
|
|
return IP_NULL;
|
|
}
|
|
|
|
return saddr_obj;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_del_saddrs_not_in_rec_cb
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_maddr_del_saddrs_not_in_rec_cb(Ipmcp_igmp_saddr_t *saddr,
|
|
Ipmcp_igmp_rec_t *rec)
|
|
{
|
|
unsigned s, num = ip_ntohs(rec->rec->num_sources);
|
|
for (s = 0; s < num; s++)
|
|
if (rec->rec->source_addresses[s].s_addr == saddr->saddr.s_addr)
|
|
return;
|
|
saddr->base.ops->dtor(&saddr->base);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_del_saddrs_not_in_rec
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_maddr_del_saddrs_not_in_rec(Ipmcp_igmp_maddr_t *self,
|
|
Ipmcp_igmp_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_igmp_maddr_del_saddrs_not_in_rec_cb,
|
|
rec);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_maddr_send_query
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmcp_igmp_maddr_send_query(Ipmcp_igmp_maddr_t *self,
|
|
Ip_u32 max_response_interval_msec,
|
|
Ipcom_set *saddrs)
|
|
{
|
|
if (self->base.netif->igmp.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_igmp_specific_query_tmo,
|
|
self,
|
|
max_response_interval_msec);
|
|
|
|
ipmcp_igmp_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_igmp_maddr_group_membership_interval
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u32
|
|
ipmcp_igmp_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->igmp.robustness_variable * self->netif->igmp.query_interval
|
|
+ ipmcp.config.query_response_interval;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_input
|
|
*===========================================================================
|
|
* Description: Processes an incoming IGMP packet or multicast route packet.
|
|
* Parameters:
|
|
* Returns: 0 = success,
|
|
* <0 = error.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_igmp_input(int fd)
|
|
{
|
|
struct Ip_msghdr m;
|
|
struct Ip_iovec iov;
|
|
struct Ip_in_pktinfo *pktinfo;
|
|
struct Ip_cmsghdr *c;
|
|
Ip_u8 cmsg_buf[IP_CMSG_SPACE(sizeof(struct Ip_in_pktinfo))];
|
|
int len;
|
|
Ipmcp_netif_t *netif;
|
|
Ipmcp_ip4_pkt_t *ip_hdr;
|
|
int ip_hdr_len, offset;
|
|
Ip_bool router_alert = IP_FALSE;
|
|
Ip_bool unicast_destination = IP_FALSE;
|
|
|
|
iov.iov_base = ipmcp.recv_buf;
|
|
iov.iov_len = sizeof(ipmcp.recv_buf);
|
|
|
|
m.msg_name = IP_NULL;
|
|
m.msg_namelen = 0;
|
|
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,"ipmcp_igmp_input: ipcom_recvmsg failed: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
return -1;
|
|
}
|
|
|
|
ip_hdr = iov.iov_base;
|
|
if (ip_hdr->p == 0)
|
|
{
|
|
if (len < (int) sizeof(struct Ip_igmpmsg))
|
|
{
|
|
/* This can only happen in the event of a stack bug or memory corruption */
|
|
IPCOM_LOG2(CRIT, "ipmcp_igmp_input: got MRT message smaller than %d, must be at least %d",
|
|
sizeof(struct Ip_igmpmsg), len);
|
|
return -1;
|
|
}
|
|
return ipmcp_igmp_mroute_input(iov.iov_base);
|
|
}
|
|
|
|
ip_assert(ip_hdr->p == IP_IPPROTO_IGMP);
|
|
ip_hdr_len = (ip_hdr->v_hl & 0x0f) << 2;
|
|
if (ip_hdr->ttl != 1 || len < ip_hdr_len + IPMCP_IGMP_MIN_PKT_LEN)
|
|
{
|
|
IPCOM_LOG0(NOTICE, "Invalid IGMP packet detected");
|
|
return 0;
|
|
}
|
|
|
|
offset = 20;
|
|
while (offset < ip_hdr_len)
|
|
{
|
|
Ipmcp_pkt_ip_opt *opt = (Ipmcp_pkt_ip_opt *) ((Ip_u8*) ip_hdr + offset);
|
|
|
|
if (opt->flag_class_num == IP_IPOPT_END)
|
|
break;
|
|
else if (opt->flag_class_num == IP_IPOPT_RA)
|
|
{
|
|
router_alert = IP_TRUE;
|
|
break;
|
|
}
|
|
else if (opt->flag_class_num == IP_IPOPT_NOOP)
|
|
{
|
|
offset++;
|
|
continue;
|
|
}
|
|
|
|
offset += opt->len;
|
|
}
|
|
|
|
c = IP_CMSG_FIRSTHDR(&m);
|
|
if (c == IP_NULL
|
|
|| c->cmsg_level != IP_IPPROTO_IP
|
|
|| c->cmsg_type != IP_IP_PKTINFO)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipmcp_igmp_input: failed to get pktinfo");
|
|
return -1;
|
|
}
|
|
|
|
pktinfo = IP_CMSG_DATA(c);
|
|
|
|
IPCOM_LOG3(DEBUG2, "IGMP message received from %s to %s on ifindex=%u",
|
|
ipcom_inet_ntop(IP_AF_INET, ip_hdr->src, ipmcp.log_buf, sizeof(ipmcp.log_buf)),
|
|
ipcom_inet_ntop(IP_AF_INET, ip_hdr->dst,
|
|
ipmcp.log_buf + IP_INET_ADDRSTRLEN,
|
|
sizeof(ipmcp.log_buf) - IP_INET_ADDRSTRLEN),
|
|
pktinfo->ipi_ifindex);
|
|
|
|
netif = ipmcp_if_find(pktinfo->ipi_ifindex);
|
|
if (netif == IP_NULL || netif->igmp.state == IPMCP_LINK_STATE_INACTIVE)
|
|
{
|
|
IPCOM_LOG2(DEBUG, "ipmcp_igmp_input: no router operation defined for interface id %u %s, dropping message",
|
|
pktinfo->ipi_ifindex, netif != IP_NULL ? netif->ifname : "");
|
|
return 0;
|
|
}
|
|
|
|
if (netif->igmp.primary_address.s_addr == ((struct Ip_in_addr *) ip_hdr->src)->s_addr)
|
|
/* Ignore messages sent from this host */
|
|
return 0;
|
|
|
|
/* If not multicast, verify if its destined to a known address on the receiving interface */
|
|
if (!IP_IN_CLASSD(pktinfo->ipi_addr.s_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_INET && ipcom_if_nametoindex(ifaddr->ifa_name) == pktinfo->ipi_ifindex)
|
|
{
|
|
struct Ip_sockaddr_in *s_in = (struct Ip_sockaddr_in *) (ifaddr->ifa_addr);
|
|
|
|
if (s_in->sin_addr.s_addr == pktinfo->ipi_addr.s_addr)
|
|
break;
|
|
}
|
|
}
|
|
|
|
ipcom_freeifaddrs(ifaddrs);
|
|
|
|
/* Unicast address on this interface? */
|
|
if (ifaddr == IP_NULL)
|
|
return 0;
|
|
|
|
/* We've got a unicast destination */
|
|
unicast_destination = IP_TRUE;
|
|
}
|
|
|
|
ipmcp_igmp_igmp_input(netif,
|
|
((Ip_u8 *)ip_hdr + ip_hdr_len),
|
|
len - ip_hdr_len,
|
|
(struct Ip_in_addr *) ip_hdr->src,
|
|
ip_hdr->tos,
|
|
router_alert,
|
|
unicast_destination);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_addr_change
|
|
*===========================================================================
|
|
* Description: An address was added or removed.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmcp_igmp_addr_change(Ipmcp_netif_t *netif, Ip_bool add)
|
|
{
|
|
Ipmcp_igmp_netif_t *i = &netif->igmp;
|
|
|
|
if (add)
|
|
{
|
|
/* Always update link-local address */
|
|
if (ipmcp_igmp_get_primary_addr(netif->ifindex, &i->primary_address) == 0)
|
|
if (netif->igmp.state == IPMCP_LINK_STATE_INACTIVE)
|
|
{
|
|
/* Our link local has now been set. Activate! */
|
|
if (netif->igmp.need_if_create)
|
|
(void)ipmcp_igmp_if_create(netif);
|
|
|
|
ipmcp_igmp_activate(netif);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (i->state != IPMCP_LINK_STATE_INACTIVE
|
|
&& ipmcp_igmp_get_primary_addr(netif->ifindex, &i->primary_address) != 0)
|
|
/* Our link-local has been removed. Deactivate! */
|
|
ipmcp_igmp_deactivate(netif);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_if_create
|
|
*===========================================================================
|
|
* Description: Creates the IGMP part of an interface.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_igmp_if_create(Ipmcp_netif_t *netif)
|
|
{
|
|
struct Ip_vifctl vifctl;
|
|
Ipmcp_igmp_data_t *d = &ipmcp.igmp;
|
|
Ipmcp_igmp_netif_t *i = &netif->igmp;
|
|
int ret;
|
|
int x;
|
|
|
|
i->need_if_create = IP_TRUE;
|
|
ret = ipmcp_igmp_get_primary_addr(netif->ifindex, &i->primary_address);
|
|
if (ret < 0 || netif->igmp.primary_address.s_addr == IP_INADDR_ANY)
|
|
/* Finish creation of this interface when/if a primary address is set */
|
|
return 0;
|
|
|
|
for (x = 0; x < IP_MAXVIFS; x++)
|
|
{
|
|
d->last_vifi = (Ip_vifi_t) ((d->last_vifi + 1) % IP_MAXVIFS);
|
|
i->vif_index = d->last_vifi;
|
|
|
|
/* Register VIF in forwarding engine */
|
|
ipcom_memset(&vifctl, 0, sizeof(vifctl));
|
|
vifctl.vifc_vifi = i->vif_index;
|
|
vifctl.vifc_lcl_addr = i->primary_address;
|
|
|
|
if (ipcom_setsockopt(d->fd,
|
|
IP_IPPROTO_IP,
|
|
IP_MRT_ADD_VIF,
|
|
&vifctl,
|
|
sizeof(vifctl)) >= 0)
|
|
break;
|
|
}
|
|
|
|
if (x == IP_MAXVIFS)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_setsockopt IP_MRT_ADD_VIF: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
return -1;
|
|
}
|
|
|
|
IPCOM_TMO2_INIT(&i->timer);
|
|
|
|
/* Create multicast address database on this interface */
|
|
i->maddr_head = ipcom_hash_new((Ipcom_hash_obj_func)ipmcp_igmp_maddr_obj_hash,
|
|
(Ipcom_hash_key_func)ipmcp_igmp_addr_key_hash,
|
|
(Ipcom_hash_cmp_func)ipmcp_igmp_maddr_compare);
|
|
|
|
i->need_if_create = IP_FALSE;
|
|
ipmcp_igmp_activate(netif);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_if_destroy
|
|
*===========================================================================
|
|
* Description: Destroy an interface descriptor for IGMP
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmcp_igmp_if_destroy(Ipmcp_netif_t *netif)
|
|
{
|
|
struct Ip_vifctl vifctl;
|
|
|
|
ipmcp_igmp_deactivate(netif);
|
|
ipcom_hash_delete(netif->igmp.maddr_head);
|
|
|
|
/* Unregister VIF in forwarding engine */
|
|
ipcom_memset(&vifctl, 0, sizeof(vifctl));
|
|
vifctl.vifc_vifi = netif->igmp.vif_index;
|
|
vifctl.vifc_lcl_addr = netif->igmp.primary_address;
|
|
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_MRT_DEL_VIF,
|
|
&vifctl,
|
|
sizeof(vifctl)) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_setsockopt IP_MRT_DEL_VIF: %s", ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_upstream_if_create
|
|
*===========================================================================
|
|
* Description: Creates the IGMP part of the upstream interface.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_igmp_upstream_if_create(Ipmcp_upstream_netif_t *netif)
|
|
{
|
|
struct Ip_vifctl vifctl;
|
|
int var;
|
|
|
|
if (ipmcp_igmp_get_primary_addr(netif->ifindex, &netif->igmp.primary_address) < 0
|
|
|| netif->igmp.primary_address.s_addr == IP_INADDR_ANY)
|
|
{
|
|
IPCOM_LOG0(WARNING, "IGMP: No primary address set on the uplink interface,"
|
|
"IPv4 multicast forwarning disabled");
|
|
return 0;
|
|
}
|
|
|
|
/* Create multicast address database on this interface */
|
|
netif->igmp.maddr_head =
|
|
ipcom_hash_new((Ipcom_hash_obj_func)ipmcp_igmp_upstream_maddr_obj_hash,
|
|
(Ipcom_hash_key_func)ipmcp_igmp_addr_key_hash,
|
|
(Ipcom_hash_cmp_func)ipmcp_igmp_upstream_maddr_compare);
|
|
|
|
if (netif->igmp.maddr_head == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipmcp_igmp_upstream_if_create: failed to create hash table");
|
|
return -1;
|
|
}
|
|
|
|
/* Use this as multicast routing socket */
|
|
var = ipcom_setsockopt(ipmcp.igmp.fd, IP_IPPROTO_IP, IP_MRT_INIT, &var, sizeof(var));
|
|
if (var < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_igmp_upstream_if_create: ipcom_setsockopt IP_MRT_INIT: %s", ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
netif->igmp.vif_index = 1;
|
|
ipmcp.igmp.last_vifi = 1;
|
|
|
|
/* Register VIF in forwarding engine */
|
|
ipcom_memset(&vifctl, 0, sizeof(vifctl));
|
|
vifctl.vifc_vifi = netif->igmp.vif_index;
|
|
vifctl.vifc_lcl_addr = netif->igmp.primary_address;
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd, IP_IPPROTO_IP,
|
|
IP_MRT_ADD_VIF, &vifctl, sizeof(vifctl)) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_igmp_upstream_if_create: ipcom_setsockopt IP_MRT_ADD_VIF: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_upstream_if_destroy
|
|
*===========================================================================
|
|
* Description:Destroy an IPv4 upstream interface.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmcp_igmp_upstream_if_destroy(Ipmcp_upstream_netif_t *netif)
|
|
{
|
|
if (netif->igmp.maddr_head != IP_NULL)
|
|
{
|
|
struct Ip_vifctl vifctl;
|
|
int i;
|
|
|
|
/* Unregister VIF in forwarding engine */
|
|
ipcom_memset(&vifctl, 0, sizeof(vifctl));
|
|
vifctl.vifc_vifi = netif->igmp.vif_index;
|
|
vifctl.vifc_lcl_addr = netif->igmp.primary_address;
|
|
|
|
if (ipcom_setsockopt(ipmcp.igmp.fd,
|
|
IP_IPPROTO_IP,
|
|
IP_MRT_DEL_VIF,
|
|
&vifctl,
|
|
sizeof(vifctl)) < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipcom_setsockopt IP_MRT_DEL_VIF: %s", ipcom_strerror(ipcom_errno));
|
|
}
|
|
|
|
/* Destroy multicast elements in hash table */
|
|
ipcom_hash_for_each(netif->igmp.maddr_head,
|
|
(Ipcom_hash_foreach_cb_func)ipmcp_igmp_upstream_maddr_delete_cb,
|
|
IP_NULL);
|
|
ipcom_hash_delete(netif->igmp.maddr_head);
|
|
|
|
i = 1;
|
|
i = ipcom_setsockopt(ipmcp.igmp.fd, IP_IPPROTO_IP, IP_MRT_DONE, &i, sizeof(i));
|
|
if (i < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_igmp_exit: ipcom_setsockopt IP_MRT_DONE: %s", ipcom_strerror(ipcom_errno));
|
|
}
|
|
}
|
|
ipcom_memset(&netif->igmp, 0, sizeof(netif->igmp));
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_init
|
|
*===========================================================================
|
|
* Description: Initializes IGMP specific global data.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmcp_igmp_init(void)
|
|
{
|
|
Ipmcp_igmp_data_t *d = &ipmcp.igmp;
|
|
int var;
|
|
int tos;
|
|
Ip_u8 ra_opt;
|
|
|
|
(void)ipcom_inet_pton(IP_AF_INET, "224.0.0.1", &d->all_hosts_addr);
|
|
|
|
d->fd = ipcom_socket(IP_AF_INET, IP_SOCK_RAW, IP_IPPROTO_IGMP);
|
|
if (d->fd == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipmcp_igmp_init: ipcom_socket(AF_INET,RAW,IPPROTO_IGMP) failed: %s",
|
|
ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
/* Packet info is needed to know which interface a packet was received on */
|
|
var = 1;
|
|
var = ipcom_setsockopt(d->fd, IP_IPPROTO_IP, IP_IP_PKTINFO, &var, sizeof(var));
|
|
if (var < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_igmp_init: ipcom_setsockopt IP_IP_PKTINFO: %s", ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* RFC 3376 page 7:
|
|
* 4. Message Formats
|
|
*
|
|
* IGMP messages are encapsulated in IPv4 datagrams, with an IP
|
|
* protocol number of 2. Every IGMP message described in this
|
|
* document is sent with an IP Time-to-Live of 1, IP Precedence of
|
|
* Internetwork Control (e.g., Type of Service 0xc0), and carries
|
|
* an IP Router Alert option [RFC-2113] in its IP header.
|
|
*/
|
|
tos = 0xc0;
|
|
var = ipcom_setsockopt(d->fd, IP_IPPROTO_IP, IP_IP_TOS, &tos, sizeof(tos));
|
|
if (var < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_igmp_init: ipcom_setsockopt(IIP_TOS): %s", ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
ra_opt = 1;
|
|
var = ipcom_setsockopt(d->fd, IP_IPPROTO_IP, IP_IP_ROUTER_ALERT, &ra_opt, sizeof(ra_opt));
|
|
if (var < 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipmcp_igmp_init: ipcom_setsockopt(IIP_TOS): %s", ipcom_strerror(ipcom_errno));
|
|
IP_PANIC();
|
|
return -1;
|
|
}
|
|
|
|
/* Create the call table for the Ipmcp_igmp_rec_t class */
|
|
d->rec_ops.get_type = (Ip_u8(*)(RTHIS)) ipmcp_igmp_rec_get_type;
|
|
d->rec_ops.get_maddr = (void*(*)(RTHIS)) ipmcp_igmp_rec_get_maddr;
|
|
d->rec_ops.get_num_saddrs = (unsigned(*)(RTHIS)) ipmcp_igmp_rec_get_num_saddrs;
|
|
d->rec_ops.get_saddr = (void*(*)(RTHIS, unsigned)) ipmcp_igmp_rec_get_saddr;
|
|
d->rec_ops.as_set = (Ipcom_set*(*)(RTHIS)) ipmcp_igmp_rec_as_set;
|
|
d->rec_ops.clear_source_list = (void(*)(RTHIS)) ipmcp_igmp_rec_clear_source_list;
|
|
|
|
/* Create the call table for the Ipmcp_igmp_saddr_t class */
|
|
ipmcp_saddr_ops_init(&d->saddr_ops);
|
|
d->saddr_ops.get_addr = (void*(*)(STHIS)) ipmcp_igmp_saddr_get_addr;
|
|
|
|
/* Create the call table for the Ipmcp_igmp_maddr_t class */
|
|
ipmcp_maddr_ops_init(&d->maddr_ops);
|
|
d->maddr_ops.dtor = (void(*)(MTHIS)) ipmcp_igmp_maddr_delete;
|
|
d->maddr_ops.get_addr = (void*(*)(MTHIS)) ipmcp_igmp_maddr_get_addr;
|
|
d->maddr_ops.compatibility_mode = (int(*)(MTHIS)) ipmcp_igmp_maddr_compatibility_mode;
|
|
d->maddr_ops.add_saddr = (Ipmcp_saddr_t*(*)(MTHIS, IP_CONST void*, Ip_bool)) ipmcp_igmp_maddr_add_saddr;
|
|
d->maddr_ops.del_saddrs_not_in_rec = (void(*)(MTHIS, Ipmcp_rec_t*, Ip_bool)) ipmcp_igmp_maddr_del_saddrs_not_in_rec;
|
|
d->maddr_ops.send_query = (void(*)(MTHIS, Ip_u32, Ipcom_set*)) ipmcp_igmp_maddr_send_query;
|
|
d->maddr_ops.group_membership_interval = (Ip_u32(*)(MTHIS)) ipmcp_igmp_maddr_group_membership_interval;
|
|
d->maddr_ops.update_upstream_if_filter = (void(*)(MTHIS)) ipmcp_igmp_update_upstream_if_filter;
|
|
|
|
/* Create the call table for the Ipmcp_igmp_upstream_maddr_t class */
|
|
d->upstream_maddr_ops.dtor = (void(*)(UTHIS))ipmcp_igmp_upstream_maddr_delete;
|
|
d->upstream_maddr_ops.get_addr = (void*(*)(UTHIS)) ipmcp_igmp_upstream_maddr_get_addr;
|
|
d->upstream_maddr_ops.change_mode = (void(*)(UTHIS, Ip_bool)) ipmcp_igmp_upstream_maddr_change_mode;
|
|
d->upstream_maddr_ops.add_source = (void(*)(UTHIS, void*)) ipmcp_igmp_upstream_maddr_add_source;
|
|
d->upstream_maddr_ops.del_source = (void(*)(UTHIS, void*)) ipmcp_igmp_upstream_maddr_del_source;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmcp_igmp_exit
|
|
*===========================================================================
|
|
* Description: Frees all resources held by the IGMP module.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmcp_igmp_exit(void)
|
|
{
|
|
(void)ipcom_socketclose(ipmcp.igmp.fd);
|
|
ipmcp.igmp.fd = IP_INVALID_SOCKET;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
#endif /* IPMCP_USE_IGMP */
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|