2 ** igmpproxy - IGMP proxy based multicast router
3 ** Copyright (C) 2005 Johnny Egeland <johnny@rlo.org>
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 **----------------------------------------------------------------------------
21 ** This software is derived work from the following software. The original
22 ** source code has been modified from it's original state by the author
25 ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de>
26 ** - Licensed under the GNU General Public License, version 2
28 ** mrouted 3.9-beta3 - COPYRIGHT 1989 by The Board of Trustees of
29 ** Leland Stanford Junior University.
30 ** - Original license can be found in the Stanford.txt file.
36 * Functions for recieveing and processing IGMP requests.
40 #include "igmpproxy.h"
43 void sendGroupSpecificMemberQuery(void *argument
);
53 * Handles incoming membership reports, and
54 * appends them to the routing table.
56 void acceptGroupReport(uint32_t src
, uint32_t group
, uint8_t type
) {
57 struct IfDesc
*sourceVif
;
59 // Sanitycheck the group adress...
60 if(!IN_MULTICAST( ntohl(group
) )) {
61 my_log(LOG_WARNING
, 0, "The group address %s is not a valid Multicast group.",
66 // Find the interface on which the report was recieved.
67 sourceVif
= getIfByAddress( src
);
68 if(sourceVif
== NULL
) {
69 my_log(LOG_WARNING
, 0, "No interfaces found for source %s",
74 if(sourceVif
->InAdr
.s_addr
== src
) {
75 my_log(LOG_NOTICE
, 0, "The IGMP message was from myself. Ignoring.");
79 // We have a IF so check that it's an downstream IF.
80 if(sourceVif
->state
== IF_STATE_DOWNSTREAM
) {
82 my_log(LOG_DEBUG
, 0, "Should insert group %s (from: %s) to route table. Vif Ix : %d",
83 inetFmt(group
,s1
), inetFmt(src
,s2
), sourceVif
->index
);
85 // If we don't have a whitelist we insertRoute and done
86 if(sourceVif
->allowedgroups
== NULL
)
88 insertRoute(group
, sourceVif
->index
);
91 // Check if this Request is legit on this interface
92 struct SubnetList
*sn
;
93 for(sn
= sourceVif
->allowedgroups
; sn
!= NULL
; sn
= sn
->next
)
94 if((group
& sn
->subnet_mask
) == sn
->subnet_addr
)
96 // The membership report was OK... Insert it into the route table..
97 insertRoute(group
, sourceVif
->index
);
100 my_log(LOG_INFO
, 0, "The group address %s may not be requested from this interface. Ignoring.", inetFmt(group
, s1
));
102 // Log the state of the interface the report was recieved on.
103 my_log(LOG_INFO
, 0, "Membership report was recieved on %s. Ignoring.",
104 sourceVif
->state
==IF_STATE_UPSTREAM
?"the upstream interface":"a disabled interface");
110 * Recieves and handles a group leave message.
112 void acceptLeaveMessage(uint32_t src
, uint32_t group
) {
113 struct IfDesc
*sourceVif
;
116 "Got leave message from %s to %s. Starting last member detection.",
117 inetFmt(src
, s1
), inetFmt(group
, s2
));
119 // Sanitycheck the group adress...
120 if(!IN_MULTICAST( ntohl(group
) )) {
121 my_log(LOG_WARNING
, 0, "The group address %s is not a valid Multicast group.",
126 // Find the interface on which the report was recieved.
127 sourceVif
= getIfByAddress( src
);
128 if(sourceVif
== NULL
) {
129 my_log(LOG_WARNING
, 0, "No interfaces found for source %s",
134 // We have a IF so check that it's an downstream IF.
135 if(sourceVif
->state
== IF_STATE_DOWNSTREAM
) {
137 GroupVifDesc
*gvDesc
;
138 gvDesc
= (GroupVifDesc
*) malloc(sizeof(GroupVifDesc
));
140 // Tell the route table that we are checking for remaining members...
141 setRouteLastMemberMode(group
);
143 // Call the group spesific membership querier...
144 gvDesc
->group
= group
;
145 gvDesc
->vifAddr
= sourceVif
->InAdr
.s_addr
;
148 sendGroupSpecificMemberQuery(gvDesc
);
151 // just ignore the leave request...
152 my_log(LOG_DEBUG
, 0, "The found if for %s was not downstream. Ignoring leave request.", inetFmt(src
, s1
));
157 * Sends a group specific member report query until the
160 void sendGroupSpecificMemberQuery(void *argument
) {
161 struct Config
*conf
= getCommonConfig();
163 // Cast argument to correct type...
164 GroupVifDesc
*gvDesc
= (GroupVifDesc
*) argument
;
166 if(gvDesc
->started
) {
167 // If aging returns false, we don't do any further action...
168 if(!lastMemberGroupAge(gvDesc
->group
)) {
175 // Send a group specific membership query...
176 sendIgmp(gvDesc
->vifAddr
, gvDesc
->group
,
177 IGMP_MEMBERSHIP_QUERY
,
178 conf
->lastMemberQueryInterval
* IGMP_TIMER_SCALE
,
181 my_log(LOG_DEBUG
, 0, "Sent membership query from %s to %s. Delay: %d",
182 inetFmt(gvDesc
->vifAddr
,s1
), inetFmt(gvDesc
->group
,s2
),
183 conf
->lastMemberQueryInterval
);
185 // Set timeout for next round...
186 timer_setTimer(conf
->lastMemberQueryInterval
, sendGroupSpecificMemberQuery
, gvDesc
);
192 * Sends a general membership query on downstream VIFs
194 void sendGeneralMembershipQuery() {
195 struct Config
*conf
= getCommonConfig();
199 // Loop through all downstream vifs...
200 for ( Ix
= 0; (Dp
= getIfByIx(Ix
)); Ix
++ ) {
201 if ( Dp
->InAdr
.s_addr
&& ! (Dp
->Flags
& IFF_LOOPBACK
) ) {
202 if(Dp
->state
== IF_STATE_DOWNSTREAM
) {
203 // Send the membership query...
204 sendIgmp(Dp
->InAdr
.s_addr
, allhosts_group
,
205 IGMP_MEMBERSHIP_QUERY
,
206 conf
->queryResponseInterval
* IGMP_TIMER_SCALE
, 0, 0);
209 "Sent membership query from %s to %s. Delay: %d",
210 inetFmt(Dp
->InAdr
.s_addr
,s1
),
211 inetFmt(allhosts_group
,s2
),
212 conf
->queryResponseInterval
);
217 // Install timer for aging active routes.
218 timer_setTimer(conf
->queryResponseInterval
, ageActiveRoutes
, NULL
);
220 // Install timer for next general query...
221 if(conf
->startupQueryCount
>0) {
222 // Use quick timer...
223 timer_setTimer(conf
->startupQueryInterval
, sendGeneralMembershipQuery
, NULL
);
224 // Decrease startup counter...
225 conf
->startupQueryCount
--;
229 timer_setTimer(conf
->queryInterval
, sendGeneralMembershipQuery
, NULL
);