523 lines
17 KiB
C
523 lines
17 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_mcast.c,v $ $Revision: 1.10 $
|
|
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_mcast.c,v $
|
|
* $Author: ulf $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
* Function that operations on the multicast control block used by the
|
|
* IGMP and MLD protocol implementation.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipnet_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_errno.h>
|
|
#include <ipcom_sysvar.h>
|
|
|
|
#include "ipnet_h.h"
|
|
#include "ipnet_mcast.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_mcast_init
|
|
*===========================================================================
|
|
* Description: Initializes a multicast control block.
|
|
* Parameters: macb - A multicast control block.
|
|
* Returns:
|
|
*
|
|
*/
|
|
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
|
|
IP_GLOBAL int
|
|
ipnet_mcast_init(Ipnet_mcast_addr *macb)
|
|
{
|
|
macb->sockets = ipcom_set_new(sizeof(Ipnet_socket *));
|
|
return (macb->sockets == IP_NULL ? -IP_ERRNO_ENOMEM : 0);
|
|
}
|
|
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_mcast_free
|
|
*===========================================================================
|
|
* Description: Frees all resources held by the multicast control block.
|
|
* Parameters: macb - A multicast control block.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_mcast_free(Ipnet_mcast_addr *macb)
|
|
{
|
|
ipnet_mcast_clear(macb);
|
|
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
|
|
ipcom_set_delete(macb->filter);
|
|
ipcom_set_delete(macb->sockets);
|
|
ipcom_set_delete(macb->specific_query_sources);
|
|
ipcom_set_delete(macb->filter_change_sources);
|
|
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_mcast_clear
|
|
*===========================================================================
|
|
* Description: Resets the filter for the multicast address to "include nothing".
|
|
* Parameters: macb - A multicast control block.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_mcast_clear(Ipnet_mcast_addr *macb)
|
|
{
|
|
ipnet_timeout_cancel(macb->specific_query_tmo);
|
|
ipnet_timeout_cancel(macb->general_query_tmo);
|
|
ipnet_timeout_cancel(macb->filter_change_tmo);
|
|
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
|
|
/* Set filter to "TO_IN {}" */
|
|
if (macb->filter == IP_NULL)
|
|
/*
|
|
* The element size is unimportant since this set will never
|
|
* contain anything
|
|
*/
|
|
macb->filter = ipcom_set_new(1);
|
|
else
|
|
ipcom_set_remove_all(macb->filter);
|
|
|
|
if (macb->filter)
|
|
macb->filter->user = IPNET_MCAST_RECORD_TYPE_CHANGE_TO_INCLUDE;
|
|
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_mcast_schedule_membership_report
|
|
*===========================================================================
|
|
* Description: Schedules sending of a report in the interval
|
|
* [0..max_delay_msec]
|
|
* Parameters: macb - A multicast control block.
|
|
* is_specific_query - IP_TRUE if this is a report as a response
|
|
* on a specific query.
|
|
* max_delay_msec - The maximum delay before the report must be
|
|
* sent.
|
|
* tmo_handler - Function to call to send the report.
|
|
* addr_entry - The address entry for a multicast address.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_mcast_schedule_membership_report(Ipnet_mcast_addr *macb,
|
|
Ip_bool is_specific_query,
|
|
Ip_u32 max_delay_msec,
|
|
Ipnet_timeout_handler tmo_handler,
|
|
void *addr_entry)
|
|
{
|
|
Ipnet_timeout **ptmo;
|
|
Ip_u32 msec1;
|
|
Ip_u32 msec2;
|
|
|
|
if (is_specific_query)
|
|
ptmo = &macb->specific_query_tmo;
|
|
else
|
|
ptmo = &macb->general_query_tmo;
|
|
|
|
if (*ptmo != IP_NULL)
|
|
{
|
|
/* Report timer already running */
|
|
if (ipnet_timeout_msec_until(*ptmo) <= max_delay_msec)
|
|
/* Will expire before the max delay, no need to reset it */
|
|
return;
|
|
ipnet_timeout_cancel(*ptmo);
|
|
}
|
|
|
|
msec1 = ipcom_rand() % max_delay_msec;
|
|
msec2 = ipcom_sysvar_get_as_int0("ipnet.mcast.min_report_delay", 0);
|
|
(void) ipnet_timeout_schedule(IP_MAX(msec1, msec2),
|
|
tmo_handler,
|
|
addr_entry,
|
|
ptmo);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_mcast_report_finish
|
|
*===========================================================================
|
|
* Description: Schedules sending of a report in the interval
|
|
* [0..max_delay_msec]
|
|
* Parameters: macb - A multicast control block.
|
|
* tmo_handler - Function to call to resend a report.
|
|
* addr_entry - The address entry for a multicast address.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_mcast_report_finish(Ipnet_mcast_addr *macb,
|
|
Ipnet_timeout_handler tmo_handler,
|
|
void *addr_entry)
|
|
{
|
|
switch (macb->report_type)
|
|
{
|
|
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
|
|
case IPNET_MCAST_REPORT_SPECIFIC_QUERY:
|
|
ipcom_set_delete(macb->specific_query_sources);
|
|
macb->specific_query_sources = IP_NULL;
|
|
break;
|
|
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
|
|
case IPNET_MCAST_REPORT_FILTER_CHANGE:
|
|
if (macb->filter_change_resend_count)
|
|
{
|
|
--macb->filter_change_resend_count;
|
|
if (ipnet_timeout_schedule(IPNET_MCAST_UNSOLICITED_REPORT_INTERVAL,
|
|
tmo_handler,
|
|
addr_entry,
|
|
&macb->filter_change_tmo) == 0)
|
|
break;
|
|
}
|
|
|
|
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
|
|
ipcom_set_delete(macb->filter_change_sources);
|
|
macb->filter_change_sources = IP_NULL;
|
|
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_mcast_build_if_filter
|
|
*===========================================================================
|
|
* Description: Builds the interface filter from the sources from a socket.
|
|
* This function should be called once for set of sources
|
|
* for every socket in the system.
|
|
* Parameters: macb - A multicast control block.
|
|
* sources - The set of sources for one socket.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_mcast_build_if_filter(Ipnet_mcast_addr *macb, Ipcom_set *sources)
|
|
{
|
|
Ipcom_set *set = macb->filter;
|
|
|
|
if (set == IP_NULL)
|
|
{
|
|
/* Use the this set as starting point */
|
|
macb->filter = ipcom_set_clone(sources);
|
|
return;
|
|
}
|
|
else if (macb->report_type == 0)
|
|
{
|
|
if (set->user == IPNET_MCAST_RECORD_TYPE_IS_INCLUDE)
|
|
{
|
|
if (sources->user == IPNET_MCAST_RECORD_TYPE_IS_EXCLUDE)
|
|
{
|
|
/* Find first exclude sets to use as a starting point */
|
|
ipcom_set_delete(set);
|
|
macb->filter = ipcom_set_clone(sources);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sources->user == IPNET_MCAST_RECORD_TYPE_IS_EXCLUDE)
|
|
/* The staring filter must contain all excluded
|
|
sources. */
|
|
ipcom_set_union(set, sources);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (set->user == IPNET_MCAST_RECORD_TYPE_IS_INCLUDE)
|
|
{
|
|
ip_assert(sources->user == IPNET_MCAST_RECORD_TYPE_IS_INCLUDE);
|
|
(void)ipcom_set_union(set, sources);
|
|
}
|
|
else
|
|
{
|
|
if (sources->user == IPNET_MCAST_RECORD_TYPE_IS_INCLUDE)
|
|
ipcom_set_subtract(set, sources);
|
|
else
|
|
ipcom_set_intersection(set, sources);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_mcast_build_source_change_report
|
|
*===========================================================================
|
|
* Description: Builds the change set for a specific multicast address
|
|
* and updates the interface filter.
|
|
* Parameters: macb - A multicast control block.
|
|
* build_if_filter_cb - Function that knows how to build the
|
|
* interface filter.
|
|
* addr_entry - Data passed to the "build_if_filter_cb".
|
|
* Returns: IP_TRUE if a report should be sent, IP_FALSE if it should not.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipnet_mcast_build_source_change_report(Ipnet_mcast_addr *macb,
|
|
Ipcom_set_foreach_cb_func build_if_filter_cb,
|
|
void *addr_entry)
|
|
{
|
|
int current_fm;
|
|
int new_fm;
|
|
int report_fm;
|
|
Ip_bool send_report = IP_FALSE;
|
|
Ipcom_set *change_set;
|
|
Ipcom_set *if_filter;
|
|
|
|
if_filter = macb->filter;
|
|
macb->filter = IP_NULL;
|
|
|
|
if (if_filter == IP_NULL)
|
|
current_fm = IPNET_MCAST_RECORD_TYPE_IS_EXCLUDE;
|
|
else
|
|
{
|
|
/* Must find one of the exclude filters as a starting point */
|
|
macb->report_type = 0;
|
|
ipcom_set_for_each(macb->sockets,
|
|
build_if_filter_cb,
|
|
addr_entry);
|
|
|
|
if (macb->filter == IP_NULL)
|
|
/* Out of memory, use exclude nothing filter for now */
|
|
goto done;
|
|
|
|
current_fm = if_filter->user;
|
|
if (current_fm == IPNET_MCAST_RECORD_TYPE_CHANGE_TO_INCLUDE)
|
|
macb->filter->user = IPNET_MCAST_RECORD_TYPE_IS_INCLUDE;
|
|
}
|
|
|
|
/*
|
|
* Build the group interface filter from all socket filters using
|
|
* this group
|
|
*/
|
|
macb->report_type = ~0;
|
|
ipcom_set_for_each(macb->sockets,
|
|
build_if_filter_cb,
|
|
addr_entry);
|
|
if (macb->filter == IP_NULL)
|
|
goto done;
|
|
|
|
new_fm = macb->filter->user;
|
|
change_set = macb->filter_change_sources;
|
|
|
|
if (new_fm != current_fm)
|
|
{
|
|
report_fm = (new_fm == IPNET_MCAST_RECORD_TYPE_IS_INCLUDE
|
|
? IPNET_MCAST_RECORD_TYPE_CHANGE_TO_INCLUDE
|
|
: IPNET_MCAST_RECORD_TYPE_CHANGE_TO_EXCLUDE);
|
|
ipcom_set_delete(change_set);
|
|
change_set = ipcom_set_clone(macb->filter);
|
|
if (change_set == IP_NULL)
|
|
/* Out of memory */
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
int add_op;
|
|
int del_op;
|
|
Ipcom_set *this_change;
|
|
Ip_bool is_add = IP_FALSE;
|
|
|
|
if (ipcom_set_equal(macb->filter, if_filter))
|
|
/* No change to report */
|
|
goto done;
|
|
|
|
if (if_filter == IP_NULL || macb->filter->obj_count > if_filter->obj_count)
|
|
is_add = IP_TRUE;
|
|
|
|
this_change = ipcom_set_clone(is_add ? macb->filter : if_filter);
|
|
if (this_change == IP_NULL)
|
|
/* Out of memory */
|
|
goto done;
|
|
|
|
if (if_filter != IP_NULL)
|
|
ipcom_set_subtract(this_change, is_add ? if_filter : macb->filter);
|
|
/*
|
|
* 'this_change' now contains the difference between the old
|
|
* and the new interface filter
|
|
*/
|
|
|
|
add_op = (new_fm == IPNET_MCAST_RECORD_TYPE_IS_INCLUDE
|
|
? IPNET_MCAST_RECORD_TYPE_ALLOW_NEW_SOURCES
|
|
: IPNET_MCAST_RECORD_TYPE_BLOCK_OLD_SOURCES);
|
|
del_op = (new_fm == IPNET_MCAST_RECORD_TYPE_IS_INCLUDE
|
|
? IPNET_MCAST_RECORD_TYPE_BLOCK_OLD_SOURCES
|
|
: IPNET_MCAST_RECORD_TYPE_ALLOW_NEW_SOURCES);
|
|
|
|
if (change_set != IP_NULL)
|
|
{
|
|
/*
|
|
* Reporting already in progress, merge the new change
|
|
* with the old report unless the report type
|
|
* different. This report will take precedence over the
|
|
* old report if they cannot be merged
|
|
*/
|
|
if (new_fm == IPNET_MCAST_RECORD_TYPE_IS_INCLUDE)
|
|
{
|
|
if (change_set->user == IPNET_MCAST_RECORD_TYPE_BLOCK_OLD_SOURCES)
|
|
del_op = change_set->user;
|
|
else if (change_set->user == IPNET_MCAST_RECORD_TYPE_CHANGE_TO_INCLUDE
|
|
|| change_set->user == IPNET_MCAST_RECORD_TYPE_ALLOW_NEW_SOURCES)
|
|
add_op = change_set->user;
|
|
}
|
|
|
|
if (new_fm == IPNET_MCAST_RECORD_TYPE_IS_EXCLUDE)
|
|
{
|
|
if (change_set->user == IPNET_MCAST_RECORD_TYPE_BLOCK_OLD_SOURCES)
|
|
add_op = change_set->user;
|
|
else if (change_set->user == IPNET_MCAST_RECORD_TYPE_ALLOW_NEW_SOURCES)
|
|
del_op = change_set->user;
|
|
}
|
|
}
|
|
|
|
report_fm = is_add ? add_op : del_op;
|
|
|
|
if (change_set != IP_NULL)
|
|
{
|
|
if (report_fm != change_set->user)
|
|
/* Reports could not be merged */
|
|
ipcom_set_delete(change_set);
|
|
else
|
|
{
|
|
/*
|
|
* The previous report could be merged with the new,
|
|
* 'change_set' must be first so the new changes is
|
|
* added at the end. The new changes will then be
|
|
* first in the report that is sent out.
|
|
*/
|
|
ipcom_set_union(change_set, this_change);
|
|
ipcom_set_delete(this_change);
|
|
this_change = change_set;
|
|
}
|
|
}
|
|
|
|
change_set = this_change;
|
|
}
|
|
|
|
change_set->user = report_fm;
|
|
macb->filter_change_sources = change_set;
|
|
send_report = IP_TRUE;
|
|
|
|
done:
|
|
ipcom_set_delete(if_filter);
|
|
return send_report;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_mcast_time_to_msec
|
|
*===========================================================================
|
|
* Description: Convert the time value used by IGMPv3 and MLDv2 to milliseconds.
|
|
* Parameters: t - The time field.
|
|
* Returns: The time in milliseconds.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_u32
|
|
ipnet_mcast_time_to_msec(Ip_u8 t)
|
|
{
|
|
if (t < 128)
|
|
return t * 1000;
|
|
|
|
/*
|
|
* 0 1 2 3 4 5 6 7
|
|
* +-+-+-+-+-+-+-+-+
|
|
* |1| exp | mant |
|
|
* +-+-+-+-+-+-+-+-+
|
|
* Time = (mant | 0x10) << (exp + 3)
|
|
*/
|
|
return 1000 * ((t & 0xf) | 0x10) << (((t & 0x70) >> 4) + 3);
|
|
}
|
|
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|