246 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
| **  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, version 2
 | |
| **  
 | |
| **  mrouted 3.9-beta3 - COPYRIGHT 1989 by The Board of Trustees of 
 | |
| **  Leland Stanford Junior University.
 | |
| **  - Original license can be found in the "doc/mrouted-LINCESE" file.
 | |
| **
 | |
| */
 | |
| /**
 | |
| *   request.c 
 | |
| *
 | |
| *   Functions for recieveing and processing IGMP requests.
 | |
| *
 | |
| */
 | |
| 
 | |
| #include "defs.h"
 | |
| 
 | |
| // Prototypes...
 | |
| void sendGroupSpecificMemberQuery(void *argument);  
 | |
|     
 | |
| typedef struct {
 | |
|     uint32      group;
 | |
|     uint32      vifAddr;
 | |
|     short       started;
 | |
| } GroupVifDesc;
 | |
| 
 | |
| 
 | |
| /**
 | |
| *   Handles incoming membership reports, and
 | |
| *   appends them to the routing table.
 | |
| */
 | |
| void acceptGroupReport(uint32 src, uint32 group, uint8 type) {
 | |
|     struct IfDesc  *sourceVif;
 | |
| 
 | |
|     // Sanitycheck the group adress...
 | |
|     if(!IN_MULTICAST( ntohl(group) )) {
 | |
|         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 recieved.
 | |
|     sourceVif = getIfByAddress( src );
 | |
|     if(sourceVif == NULL) {
 | |
|         log(LOG_WARNING, 0, "No interfaces found for source %s",
 | |
|             inetFmt(src,s1));
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if(sourceVif->InAdr.s_addr == src) {
 | |
|         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) {
 | |
| 
 | |
|         IF_DEBUG log(LOG_DEBUG, 0, "Should insert group %s (from: %s) to route table. Vif Ix : %d",
 | |
|             inetFmt(group,s1), inetFmt(src,s2), sourceVif->index);
 | |
| 
 | |
|         // The membership report was OK... Insert it into the route table..
 | |
|         insertRoute(group, sourceVif->index);
 | |
| 
 | |
| 
 | |
|     } else {
 | |
|         // Log the state of the interface the report was recieved on.
 | |
|         log(LOG_INFO, 0, "Mebership report was recieved on %s. Ignoring.",
 | |
|             sourceVif->state==IF_STATE_UPSTREAM?"the upstream interface":"a disabled interface");
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
| *   Recieves and handles a group leave message.
 | |
| */
 | |
| void acceptLeaveMessage(uint32 src, uint32 group) {
 | |
|     struct IfDesc   *sourceVif;
 | |
|     
 | |
|     IF_DEBUG 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) )) {
 | |
|         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 recieved.
 | |
|     sourceVif = getIfByAddress( src );
 | |
|     if(sourceVif == NULL) {
 | |
|         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...
 | |
|         IF_DEBUG log(LOG_DEBUG, 0, "The found if for %s was not downstream. Ignoring leave request.");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
| *   Sends a group specific member report query until the 
 | |
| *   group times out...
 | |
| */
 | |
| void sendGroupSpecificMemberQuery(void *argument) {
 | |
|     struct  Config  *conf = getCommonConfig();
 | |
| 
 | |
|     // 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)) {
 | |
|             return;
 | |
|         }
 | |
|     } else {
 | |
|         gvDesc->started = 1;
 | |
|     }
 | |
| 
 | |
|     // Send a group specific membership query...
 | |
|     sendIgmp(gvDesc->vifAddr, gvDesc->group, 
 | |
|              IGMP_MEMBERSHIP_QUERY,
 | |
|              conf->lastMemberQueryInterval * IGMP_TIMER_SCALE, 
 | |
|              gvDesc->group, 0);
 | |
| 
 | |
|     IF_DEBUG log(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d",
 | |
|         inetFmt(gvDesc->vifAddr,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() {
 | |
|     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);
 | |
|                 
 | |
|                 IF_DEBUG 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, ageActiveRoutes, NULL);
 | |
| 
 | |
|     // Install timer for next general query...
 | |
|     if(conf->startupQueryCount>0) {
 | |
|         // Use quick timer...
 | |
|         timer_setTimer(conf->startupQueryInterval, sendGeneralMembershipQuery, NULL);
 | |
|         // Decrease startup counter...
 | |
|         conf->startupQueryCount--;
 | |
|     } 
 | |
|     else {
 | |
|         // Use slow timer...
 | |
|         timer_setTimer(conf->queryInterval, sendGeneralMembershipQuery, NULL);
 | |
|     }
 | |
| 
 | |
| 
 | |
| }
 | |
| #ifdef TCSUPPORT_IGMP_QOS
 | |
| void sendGeneralMembershipQueryBySignal()
 | |
| {
 | |
| 	struct  Config  *conf = getCommonConfig();
 | |
|     struct  IfDesc  *Dp;
 | |
|     int             Ix;
 | |
| 
 | |
| 	//tcdbg_printf("send igmp query by signal.\n");
 | |
| 	
 | |
|     // 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);
 | |
|                 
 | |
|                 IF_DEBUG 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);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| #endif
 | |
| 
 |