blob: 13d92ff72b58ff83353e352217e7d7e7288f8142 [file] [log] [blame]
/*
** igmpproxy - IGMP proxy based multicast router
** Copyright (C) 2005 Johnny Egeland <johnny@rlo.org>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**
**----------------------------------------------------------------------------
**
** This software is derived work from the following software. The original
** source code has been modified from it's original state by the author
** of igmpproxy.
**
** smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de>
** - Licensed under the GNU General Public License, either version 2 or
** any later version.
**
** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of
** Leland Stanford Junior University.
** - Licensed under the 3-clause BSD license, see Stanford.txt file.
**
*/
/**
* request.c
*
* Functions for recieveing and processing IGMP requests.
*
*/
#include "igmpproxy.h"
// Prototypes...
void sendGroupSpecificMemberQuery(void *argument);
typedef struct {
uint32_t group;
// uint32_t vifAddr;
short started;
} GroupVifDesc;
/**
* Handles incoming membership reports, and
* appends them to the routing table.
*/
void acceptGroupReport(uint32_t src, uint32_t group) {
struct IfDesc *sourceVif;
// Sanitycheck the group adress...
if(!IN_MULTICAST( ntohl(group) )) {
my_log(LOG_WARNING, 0, "The group address %s is not a valid Multicast group.",
inetFmt(group, s1));
return;
}
// Find the interface on which the report was received.
sourceVif = getIfByAddress( src );
if(sourceVif == NULL) {
my_log(LOG_WARNING, 0, "No interfaces found for source %s",
inetFmt(src,s1));
return;
}
if(sourceVif->InAdr.s_addr == src) {
my_log(LOG_NOTICE, 0, "The IGMP message was from myself. Ignoring.");
return;
}
// We have a IF so check that it's an downstream IF.
if(sourceVif->state == IF_STATE_DOWNSTREAM) {
my_log(LOG_DEBUG, 0, "Should insert group %s (from: %s) to route table. Vif Ix : %d",
inetFmt(group,s1), inetFmt(src,s2), sourceVif->index);
// If we don't have a whitelist we insertRoute and done
if(sourceVif->allowedgroups == NULL)
{
insertRoute(group, sourceVif->index);
return;
}
// Check if this Request is legit on this interface
struct SubnetList *sn;
for(sn = sourceVif->allowedgroups; sn != NULL; sn = sn->next)
if((group & sn->subnet_mask) == sn->subnet_addr)
{
// The membership report was OK... Insert it into the route table..
insertRoute(group, sourceVif->index);
return;
}
my_log(LOG_INFO, 0, "The group address %s may not be requested from this interface. Ignoring.", inetFmt(group, s1));
} else {
// Log the state of the interface the report was received on.
my_log(LOG_INFO, 0, "Mebership report was received on %s. Ignoring.",
sourceVif->state==IF_STATE_UPSTREAM?"the upstream interface":"a disabled interface");
}
}
/**
* Recieves and handles a group leave message.
*/
void acceptLeaveMessage(uint32_t src, uint32_t group) {
struct IfDesc *sourceVif;
my_log(LOG_DEBUG, 0,
"Got leave message from %s to %s. Starting last member detection.",
inetFmt(src, s1), inetFmt(group, s2));
// Sanitycheck the group adress...
if(!IN_MULTICAST( ntohl(group) )) {
my_log(LOG_WARNING, 0, "The group address %s is not a valid Multicast group.",
inetFmt(group, s1));
return;
}
// Find the interface on which the report was received.
sourceVif = getIfByAddress( src );
if(sourceVif == NULL) {
my_log(LOG_WARNING, 0, "No interfaces found for source %s",
inetFmt(src,s1));
return;
}
// We have a IF so check that it's an downstream IF.
if(sourceVif->state == IF_STATE_DOWNSTREAM) {
GroupVifDesc *gvDesc;
gvDesc = (GroupVifDesc*) malloc(sizeof(GroupVifDesc));
// Tell the route table that we are checking for remaining members...
setRouteLastMemberMode(group);
// Call the group spesific membership querier...
gvDesc->group = group;
// gvDesc->vifAddr = sourceVif->InAdr.s_addr;
gvDesc->started = 0;
sendGroupSpecificMemberQuery(gvDesc);
} else {
// just ignore the leave request...
my_log(LOG_DEBUG, 0, "The found if for %s was not downstream. Ignoring leave request.", inetFmt(src, s1));
}
}
/**
* Sends a group specific member report query until the
* group times out...
*/
void sendGroupSpecificMemberQuery(void *argument) {
struct Config *conf = getCommonConfig();
struct IfDesc *Dp;
int Ix;
// Cast argument to correct type...
GroupVifDesc *gvDesc = (GroupVifDesc*) argument;
if(gvDesc->started) {
// If aging returns false, we don't do any further action...
if(!lastMemberGroupAge(gvDesc->group)) {
// FIXME: Should we free gvDesc here?
return;
}
} else {
gvDesc->started = 1;
}
/**
* FIXME: This loops through all interfaces the group is active on an sends queries.
* It might be better to send only a query on the interface the leave was accepted on and remove only that interface from the route.
*/
// Loop through all downstream interfaces
for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) {
if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) {
if(Dp->state == IF_STATE_DOWNSTREAM) {
// Is that interface used in the group?
if (interfaceInRoute(gvDesc->group ,Dp->index)) {
// Send a group specific membership query...
sendIgmp(Dp->InAdr.s_addr, gvDesc->group,
IGMP_MEMBERSHIP_QUERY,
conf->lastMemberQueryInterval * IGMP_TIMER_SCALE,
gvDesc->group, 0);
my_log(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d",
inetFmt(Dp->InAdr.s_addr,s1), inetFmt(gvDesc->group,s2),
conf->lastMemberQueryInterval);
}
}
}
}
// Set timeout for next round...
timer_setTimer(conf->lastMemberQueryInterval, sendGroupSpecificMemberQuery, gvDesc);
}
/**
* Sends a general membership query on downstream VIFs
*/
void sendGeneralMembershipQuery(void) {
struct Config *conf = getCommonConfig();
struct IfDesc *Dp;
int Ix;
// Loop through all downstream vifs...
for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) {
if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) {
if(Dp->state == IF_STATE_DOWNSTREAM) {
// Send the membership query...
sendIgmp(Dp->InAdr.s_addr, allhosts_group,
IGMP_MEMBERSHIP_QUERY,
conf->queryResponseInterval * IGMP_TIMER_SCALE, 0, 0);
my_log(LOG_DEBUG, 0,
"Sent membership query from %s to %s. Delay: %d",
inetFmt(Dp->InAdr.s_addr,s1),
inetFmt(allhosts_group,s2),
conf->queryResponseInterval);
}
}
}
// Install timer for aging active routes.
timer_setTimer(conf->queryResponseInterval, (timer_f)ageActiveRoutes, NULL);
// Install timer for next general query...
if(conf->startupQueryCount>0) {
// Use quick timer...
timer_setTimer(conf->startupQueryInterval, (timer_f)sendGeneralMembershipQuery, NULL);
// Decrease startup counter...
conf->startupQueryCount--;
}
else {
// Use slow timer...
timer_setTimer(conf->queryInterval, (timer_f)sendGeneralMembershipQuery, NULL);
}
}