1/* 2** igmpproxy - IGMP proxy based multicast router 3** Copyright (C) 2005 Johnny Egeland <johnny@rlo.org> 4** 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. 9** 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. 14** 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 18** 19**---------------------------------------------------------------------------- 20** 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 23** of igmpproxy. 24** 25** smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de> 26** - Licensed under the GNU General Public License, version 2 27** 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 "doc/mrouted-LINCESE" file. 31** 32*/ 33/** 34* request.c 35* 36* Functions for recieveing and processing IGMP requests. 37* 38*/ 39 40#include "defs.h" 41 42#ifdef USE_ATH_HEADER 43static int startupQueryCount = 0; 44#endif 45// Prototypes... 46void sendGroupSpecificMemberQuery(void *argument); 47 48typedef struct { 49 uint32 group; 50 uint32 vifAddr; 51 short started; 52} GroupVifDesc; 53 54#if (SUPPURT_MCAST_TO_UNICAST) 55inline int 56join_group(unsigned long src, unsigned long group) 57{ 58 char line[64]; 59 sprintf(line, "echo \"a %d.%d.%d.%d %d.%d.%d.%d\" > /proc/mcast", 60 NIPQUAD(src), NIPQUAD(group)); 61// DPRINTF("%s\n",line); 62 system(line); 63 return 0; 64} 65#endif 66 67 68/** 69* Handles incoming membership reports, and 70* appends them to the routing table. 71*/ 72#ifdef USE_ATH_HEADER 73void acceptGroupReport(uint32 port, uint32 src, uint32 group, uint8 type) { 74#else 75void acceptGroupReport(uint32 src, uint32 group, uint8 type) { 76#endif 77 struct IfDesc *sourceVif; 78 79 // Sanitycheck the group adress... 80 if(!IN_MULTICAST( ntohl(group) )) { 81 igmp_syslog(LOG_WARNING, 0, "The group address %s is not a valid Multicast group.", 82 inetFmt(group, s1)); 83 return; 84 } 85 // add by Kane, skipped control group 86 // ex. 224.0.0.x or 239.255.255.250 87 if (mc_ctrl_packet(ntohl(group))) { 88// DPRINTF("%s :: skipped group [%x]\n", __FUNCTION__, group); 89 return ; 90 } 91#ifdef USE_ATH_HEADER 92 /* Bug 755: in other mode, if the bridge ip address and the frame from downsteam 93 * is not in the same network, all the join request will be accepted. 94 * We don't care the source ip address in the same network or not. */ 95// we do not accept the frame from downsteam is not in the same network to move the code segments to the insertRoute function 96/* 97 if(commonConfig.mode&0x01){ 98 IF_DEBUG atlog(LOG_DEBUG, 0, 99 "Should insert group %s port %d to fdb table.", 100 inetFmt(group, s1),port); 101 if (port > 0) 102 insertFdb(group, port); 103 } 104*/ 105#endif 106 // Find the interface on which the report was recieved. 107 sourceVif = getIfByAddress( src ); 108 if(sourceVif == NULL) { 109 igmp_syslog(LOG_WARNING, 0, "No interfaces found for source %s", 110 inetFmt(src,s1)); 111 return; 112 } 113 114 if(sourceVif->InAdr.s_addr == src) { 115 igmp_syslog(LOG_NOTICE, 0, "The IGMP message was from myself. Ignoring."); 116 return; 117 } 118 119 // We have a IF so check that it's an downstream IF. 120 if(sourceVif->state == IF_STATE_DOWNSTREAM) { 121#ifdef USE_ATH_HEADER 122 // The membership report was OK... Insert it into the route table.. 123 if(commonConfig.mode&0x02){ 124#endif 125 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Should insert group %s (from: %s) to route table. Vif Ix : %d", 126 inetFmt(group,s1), inetFmt(src,s2), sourceVif->index); 127 128#if (SUPPORT_IGMPV3) 129 if ((type == IGMP_V1_MEMBERSHIP_REPORT) || (type == IGMP_V2_MEMBERSHIP_REPORT)) 130 { 131 group_compatibilty_mode = IGMPV1_V2; 132 } 133#endif 134 135 // The membership report was OK... Insert it into the route table.. 136#if (IGMP_IMMEDIATE_LEAVE) 137#ifdef USE_ATH_HEADER 138 insertRoute(port, group, sourceVif->index, src); 139#else 140 insertRoute(group, sourceVif->index, src); 141#endif 142#else 143 insertRoute(group, sourceVif->index); 144#endif 145#if (SUPPURT_MCAST_TO_UNICAST) 146 join_group(src, group); 147#endif 148#ifdef USE_ATH_HEADER 149 } 150#endif 151 } else { 152 // Log the state of the interface the report was recieved on. 153 igmp_syslog(LOG_INFO, 0, "Mebership report was recieved on %s. Ignoring.", 154 sourceVif->state==IF_STATE_UPSTREAM?"the upstream interface":"a disabled interface"); 155 } 156 157} 158 159/** 160* Recieves and handles a group leave message. 161*/ 162void acceptLeaveMessage(uint32 src, uint32 group) { 163 struct IfDesc *sourceVif; 164 165 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Got leave message from %s to %s. Starting last member detection.", 166 inetFmt(src, s1), inetFmt(group, s2)); 167 168 // Sanitycheck the group adress... 169 if(!IN_MULTICAST( ntohl(group) )) { 170 igmp_syslog(LOG_WARNING, 0, "The group address %s is not a valid Multicast group.", 171 inetFmt(group, s1)); 172 return; 173 } 174 // add by Kane, skipped control group 175 // ex. 224.0.0.x or 239.255.255.250 176 if (mc_ctrl_packet(ntohl(group))) { 177 return ; 178 } 179 // Find the interface on which the report was recieved. 180 sourceVif = getIfByAddress( src ); 181 if(sourceVif == NULL) { 182 igmp_syslog(LOG_WARNING, 0, "No interfaces found for source %s", 183 inetFmt(src,s1)); 184 return; 185 } 186 187 // We have a IF so check that it's an downstream IF. 188 if(sourceVif->state == IF_STATE_DOWNSTREAM) { 189 190 GroupVifDesc *gvDesc; 191 gvDesc = (GroupVifDesc*) malloc(sizeof(GroupVifDesc)); 192 193 // Tell the route table that we are checking for remaining members... 194#if (IGMP_IMMEDIATE_LEAVE) 195 setRouteLastMemberMode(group, src); 196#else 197 setRouteLastMemberMode(group); 198#endif 199 // Call the group spesific membership querier... 200 gvDesc->group = group; 201 gvDesc->vifAddr = sourceVif->InAdr.s_addr; 202 gvDesc->started = 0; 203 204// modified by kane, to make igmp snooping in realtek board work smoothly 205#if (!IGMP_IMMEDIATE_LEAVE) 206 sendGroupSpecificMemberQuery(gvDesc); 207#endif 208 209 } else { 210 // just ignore the leave request... 211 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "The found if for %s was not downstream. Ignoring leave request."); 212 } 213} 214 215/** 216* Sends a group specific member report query until the 217* group times out... 218*/ 219void sendGroupSpecificMemberQuery(void *argument) { 220 struct Config *conf = getCommonConfig(); 221 222 // Cast argument to correct type... 223 GroupVifDesc *gvDesc = (GroupVifDesc*) argument; 224 225 if(gvDesc->started) { 226 // If aging returns false, we don't do any further action... 227 if(!lastMemberGroupAge(gvDesc->group)) { 228 return; 229 } 230 } else { 231 gvDesc->started = 1; 232 } 233 234 // Send a group specific membership query... 235 sendIgmp(gvDesc->vifAddr, gvDesc->group, 236 IGMP_MEMBERSHIP_QUERY, 237 conf->lastMemberQueryInterval * IGMP_TIMER_SCALE, 238 gvDesc->group, 0); 239 240 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d", 241 inetFmt(gvDesc->vifAddr,s1), inetFmt(gvDesc->group,s2), 242 conf->lastMemberQueryInterval); 243 244 // Set timeout for next round... 245 timer_setTimer(conf->lastMemberQueryInterval, sendGroupSpecificMemberQuery, gvDesc); 246 247} 248 249#ifdef USE_ATH_HEADER 250void setStartCount() 251{ 252 struct Config *conf = getCommonConfig(); 253 startupQueryCount = conf->startupQueryCount; 254} 255#endif 256 257/** 258* Sends a general membership query on downstream VIFs 259*/ 260#ifndef USE_ATH_HEADER 261extern struct RouteTable *routing_table; 262#endif 263void sendGeneralMembershipQuery() { 264 struct Config *conf = getCommonConfig(); 265 struct IfDesc *Dp; 266 int Ix; 267 268 // Loop through all downstream vifs... 269 for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) { 270 if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { 271 if(Dp->state == IF_STATE_DOWNSTREAM) { 272 // Send the membership query... 273 sendIgmp(Dp->InAdr.s_addr, allhosts_group, 274 IGMP_MEMBERSHIP_QUERY, 275 conf->queryResponseInterval * IGMP_TIMER_SCALE, 0, 0); 276 277 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d", 278 inetFmt(Dp->InAdr.s_addr,s1), inetFmt(allhosts_group,s2), 279 conf->queryResponseInterval); 280 } 281 } 282 } 283 284#ifndef USE_ATH_HEADER 285 // Install timer for aging active routes. 286 timer_setTimer(conf->queryResponseInterval, ageActiveRoutes, NULL); 287 288 // Install timer for next general query... 289 if(conf->startupQueryCount>0) { 290 // Use quick timer... 291 timer_setTimer(conf->startupQueryInterval, sendGeneralMembershipQuery, NULL); 292 // Decrease startup counter... 293 conf->startupQueryCount--; 294 } 295 else { 296 // Use slow timer... 297 timer_setTimer(conf->queryInterval, sendGeneralMembershipQuery, NULL); 298 } 299#endif 300 301} 302#define LOCAL_CONTROL_START 0xE0000000 303#define LOCAL_CONTROL_END 0XE00000FF 304#define SSDP 0XEFFFFFFA 305int mc_ctrl_packet(uint32 group) 306{ 307 if (group >= LOCAL_CONTROL_START && group <= LOCAL_CONTROL_END) { 308 return 1; 309 } 310 else if (group == SSDP) { 311 return 1; 312 } 313 return 0; 314} 315 316#ifdef USE_ATH_HEADER 317/** 318* Sends a general membership query on downstream VIFs 319*/ 320void 321igmpTimerHandle() 322{ 323 struct Config *conf = getCommonConfig(); 324 325 //if (commonConfig.mode&0x02) 326 sendGeneralMembershipQuery(); 327 328 // Install timer for aging active routes. 329 if (startupQueryCount == 0 || startupQueryCount == conf->startupQueryCount) { 330 if (commonConfig.mode&0x02) 331 timer_setTimer(conf->queryResponseInterval, ageActiveRoutes, NULL); 332 333// if (commonConfig.mode&0x01) 334// timer_setTimer(conf->queryResponseInterval, ageActiveFdbs, NULL); 335 } 336 337 // Install timer for next general query... 338 if (startupQueryCount > 0) { 339 // Use quick timer... 340 timer_setTimer(conf->startupQueryInterval, igmpTimerHandle, 341 NULL); 342 // Decrease startup counter... 343 startupQueryCount--; 344 } else { 345 // Use slow timer... 346 timer_setTimer(conf->queryInterval, igmpTimerHandle, NULL); 347 } 348} 349#endif 350 351/* This function is used for BT IGMP to receive IGMP Query */ 352void accept_membership_query(uint32 src, uint32 dst, uint32 group, int code) 353{ 354 struct IfDesc *Dp; 355 int Ix; 356 357 if(src != htonl(get_if_ip(LAN_IF))) { 358 for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) { 359 if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { 360 if(Dp->state == IF_STATE_DOWNSTREAM) { 361 // Send the membership query... 362 sendIgmp(Dp->InAdr.s_addr, dst, IGMP_MEMBERSHIP_QUERY, code, group, 0); 363 364 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Sent membership query from %s to %s.", 365 inetFmt(Dp->InAdr.s_addr,s1), inetFmt(dst,s2)); 366 } 367 } 368 } 369 } 370} 371 372