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* rttable.c 35* 36* Updates the routingtable according to 37* recieved request. 38*/ 39 40#include "defs.h" 41 42 43 44 45// Keeper for the routing table... 46#ifdef USE_ATH_HEADER 47static struct RouteTable *routing_table; 48#else 49struct RouteTable *routing_table; 50#endif 51 52// Prototypes 53void logRouteTable(char *header); 54int internAgeRoute(struct RouteTable* croute); 55 56// Socket for sending join or leave requests. 57int mcGroupSock = 0; 58 59#if (0) 60void v2_join_group(int port, uint32 mgroup) 61{ 62 char line[64]; 63 if (port > 0) 64 { 65 sprintf(line,"ebtables -I port%d -p ipv4 --ip-dst %u.%u.%u.%u -j ACCEPT", port, NIPQUAD(mgroup)); 66 system(line); 67 } 68} 69 70void v2_leave_group(int port, uint32 mgroup) 71{ 72 char line[64]; 73 if (port > 0) 74 { 75 sprintf(line,"ebtables -D port%d -p ipv4 --ip-dst %u.%u.%u.%u -j ACCEPT", port, NIPQUAD(mgroup)); 76 system(line); 77 } 78} 79 80void v2_port_update (struct RouteTable *croute, int oldport, int newport) 81{ 82 uint32 port[PORT_MAX_NUM+1]; 83 memcpy(port, croute->port, sizeof(uint32)*(PORT_MAX_NUM+1)); 84 port[newport]++; 85 if (port[oldport]) port[oldport]--; 86 if (port[oldport] == 0) 87 v2_leave_group(oldport, croute->group); 88 if (port[newport] == 1) 89 v2_join_group(newport, croute->group); 90 memcpy( croute->port, port, sizeof(uint32)*(PORT_MAX_NUM+1)); 91} 92#endif 93 94#ifdef USE_ATH_HEADER 95int internUpdateKernelRoute(struct RouteTable *route, int activate); 96static inline void 97update_port(struct RouteTable *croute, uint32 oldport, uint32 newport) 98{ 99 uint32 i, ifbits = 0, port[PORT_MAX_NUM+1]; 100 memcpy(port, croute->port, sizeof(uint32)*(PORT_MAX_NUM+1)); 101 port[newport]++; 102 if (port[oldport]) port[oldport]--; 103 104 if ((port[newport] == 1) || (port[oldport] == 0)) 105 { 106 DPRINTF("%s :: group [%x] port snoop status changed, to update the fdb table....\n", __FUNCTION__, croute->group); 107 for (i = 1; i < PORT_MAX_NUM+1; i++) 108 { 109 if (port[i]) 110 ifbits |= (1<<i); 111 } 112 internUpdateFdb(croute->group, ifbits, 1); 113 } 114 memcpy( croute->port, port, sizeof(uint32)*(PORT_MAX_NUM+1)); 115} 116#endif 117/** 118* Function for retrieving the Multicast Group socket. 119*/ 120int getMcGroupSock() { 121 if( ! mcGroupSock ) { 122 mcGroupSock = openUdpSocket( INADDR_ANY, 0 );; 123 } 124 return mcGroupSock; 125} 126 127/** 128* Initializes the routing table. 129*/ 130void initRouteTable() { 131 unsigned Ix; 132 struct IfDesc *Dp; 133 134 // Clear routing table... 135 routing_table = NULL; 136 137 // Join the all routers group on downstream vifs... 138 for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) { 139 // If this is a downstream vif, we should join the All routers group... 140 if( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state == IF_STATE_DOWNSTREAM) { 141 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s", 142 inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); 143 144 joinMcGroup( getMcGroupSock(), Dp, allrouters_group ); 145 146#if (SUPPORT_IGMPV3) 147 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s", 148 inetFmt(all_group_igmpv3,s1),inetFmt(Dp->InAdr.s_addr,s2)); 149 150 joinMcGroup( getMcGroupSock(), Dp, all_group_igmpv3 ); 151#endif 152 } 153 } 154} 155 156/** 157* Internal function to send join or leave requests for 158* a specified route upstream... 159*/ 160void sendJoinLeaveUpstream(struct RouteTable* route, int join) { 161 struct IfDesc* upstrIf; 162 163 // Get the upstream VIF... 164 upstrIf = getIfByIx( upStreamVif ); 165 if(upstrIf == NULL) { 166 igmp_syslog(LOG_ERR, 0 ,"FATAL: Unable to get Upstream IF."); 167 } 168 169 IF_DEBUG { 170 igmp_syslog(LOG_DEBUG, 0, "Upstream IF addr : %s", inetFmt(upstrIf->InAdr.s_addr,s1)); 171 igmp_syslog(LOG_DEBUG, 0, "Upstream IF state : %d", upstrIf->state); 172 igmp_syslog(LOG_DEBUG, 0, "Upstream IF index : %d", upstrIf->index); 173 } 174 175 // Send join or leave request... 176 if(join) { 177 178 // Only join a group if there are listeners downstream... 179 if(route->vifBits > 0) { 180 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s", 181 inetFmt(route->group, s1), 182 inetFmt(upstrIf->InAdr.s_addr, s2)); 183 184 //k_join(route->group, upstrIf->InAdr.s_addr); 185 joinMcGroup( getMcGroupSock(), upstrIf, route->group ); 186 187 route->upstrState = ROUTESTATE_JOINED; 188 } else IF_DEBUG { 189 igmp_syslog(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", 190 inetFmt(route->group, s1)); 191 } 192 193 } else { 194 // Only leave if group is not left already... 195 if(route->upstrState != ROUTESTATE_NOTJOINED) { 196 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s", 197 inetFmt(route->group, s1), 198 inetFmt(upstrIf->InAdr.s_addr, s2)); 199 200 leaveMcGroup( getMcGroupSock(), upstrIf, route->group ); 201 202 route->upstrState = ROUTESTATE_NOTJOINED; 203 } 204 } 205} 206 207/** 208* Clear all routes from routing table, and alerts Leaves upstream. 209*/ 210void clearAllRoutes() { 211 struct RouteTable *croute, *remainroute; 212 213 // Loop through all routes... 214 for(croute = routing_table; croute; croute = remainroute) { 215 216 remainroute = croute->nextroute; 217 218 // Log the cleanup in debugmode... 219 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Removing route entry for %s", 220 inetFmt(croute->group, s1)); 221 222 // Uninstall current route 223 if(!internUpdateKernelRoute(croute, 0)) { 224 igmp_syslog(LOG_WARNING, 0, "The removal from Kernel failed."); 225 } 226 227 // Send Leave message upstream. 228 sendJoinLeaveUpstream(croute, 0); 229 230 // Clear memory, and set pointer to next route... 231 free(croute); 232 } 233 routing_table = NULL; 234 235 // Send a notice that the routing table is empty... 236 igmp_syslog(LOG_NOTICE, 0, "All routes removed. Routing table is empty."); 237} 238 239/** 240* Private access function to find a route from a given 241* Route Descriptor. 242*/ 243struct RouteTable *findRoute(uint32 group) { 244 struct RouteTable* croute; 245 246 for(croute = routing_table; croute; croute = croute->nextroute) { 247 if(croute->group == group) { 248 return croute; 249 } 250 } 251 252 return NULL; 253} 254 255#if (SUPPURT_MCAST_TO_UNICAST) 256static inline int 257leave_group(unsigned long src, unsigned long group) 258{ 259 char line[64]; 260 sprintf(line, "echo \"d %d.%d.%d.%d %d.%d.%d.%d\" > /proc/mcast", 261 NIPQUAD(src), NIPQUAD(group)); 262// DPRINTF("%s\n",line); 263 system(line); 264 return 0; 265} 266#endif 267 268#if (IGMP_IMMEDIATE_LEAVE) 269void update_listener(struct RouteTable* croute) 270{ 271 struct Listener *listener; 272 struct Listener *nlistener = NULL; 273 uint32 now = GetSysUpTime(); 274 listener = croute->listeners; 275 276 while (listener) 277 { 278 nlistener = listener->nextlistener; 279 if (now - listener->timeout >= IGMP_MBR_TIMEOUT) 280 setRouteLastMemberMode(croute->group, listener->srcAddr); 281 listener = nlistener; 282 } 283} 284 285void check_mbr_timeout(void) 286{ 287 struct RouteTable* croute = routing_table; 288 struct RouteTable* nroute = NULL; 289 290 while(croute) 291 { 292 nroute = croute->nextroute; 293 update_listener(croute); 294 croute = nroute; 295 } 296 297 timer_setTimer( INTERVAL_QUERY_RESPONSE, check_mbr_timeout, NULL); 298} 299 300struct Listener *findListener(struct RouteTable* croute, uint32 src) 301{ 302 struct Listener* listener; 303 304 for(listener = croute->listeners; listener; listener = listener->nextlistener) { 305 if(listener->srcAddr == src) { 306 listener->timeout = GetSysUpTime(); 307 return listener; 308 } 309 } 310 311 return NULL; 312} 313#ifdef USE_ATH_HEADER 314struct Listener* insertListener(uint32 port, struct RouteTable* croute, uint32 src) { 315#else 316void insertListener(struct RouteTable* croute, uint32 src) { 317#endif 318 struct Listener* newlistener; 319 320 newlistener = (struct Listener*)malloc(sizeof(struct Listener)); 321 newlistener->nextlistener = NULL; 322 newlistener->srcAddr = src; 323 newlistener->timeout = GetSysUpTime(); 324#ifdef USE_ATH_HEADER 325 newlistener->port = port; 326#if !(DNI_MULTI_LAN_SUPPORT) 327 update_port(croute, 0, port); 328#endif 329#endif 330 if(croute->listeners == NULL) 331 { 332 croute->listeners = newlistener; 333 } 334 else 335 { 336 struct Listener* listener; 337 338 listener = croute->listeners; 339 340 for(listener = croute->listeners; listener; listener = listener->nextlistener) 341 { 342 if(listener->nextlistener == NULL) 343 { 344 listener->nextlistener = newlistener; 345 break; 346 } 347 } 348 } 349#if (DNI_MULTI_LAN_SUPPORT)&&(SUPPORT_IGMPV3) 350 newlistener->mode = st_is_ex; 351 newlistener->version = IGMP_V2_MEMBERSHIP_REPORT; 352 memcpy(&newlistener->source, &ZEROSET, sizeof(ZEROSET)); 353#endif 354 return newlistener; 355} 356 357int removeListener(struct RouteTable* croute, uint32 src) 358{ 359 struct Listener* listener; 360 struct Listener* prevlistener; 361 int result = 0; 362 363 if(croute->listeners == NULL) 364 return 0; 365 366 367 prevlistener = NULL; 368 listener = croute->listeners; 369 while(listener != NULL) 370 { 371 if(listener->srcAddr == src) 372 break; 373 374 prevlistener = listener; 375 listener = listener->nextlistener; 376 } 377// modified by Kane 378// to avoid some abnormal conditions 379// if client A joined the group G and client B leaved the group G without joining, 380// the igmpproxy will be crashed 381 if (listener == NULL) 382 { 383 DPRINTF("%s :: listener is null ... group [%x] has entry [%d]\n", __FUNCTION__, 384 croute->group, (croute->listeners)?1:0); 385 if (croute->listeners) // there are some clients joined the group 386 return 1; 387 else 388 return 0; // there is no client joined the group 389 } 390#ifdef USE_ATH_HEADER 391#if !(DNI_MULTI_LAN_SUPPORT) 392 update_port(croute, listener->port, 0); 393#endif 394#endif 395 // Free the memory 396 if((prevlistener == NULL) && (listener->nextlistener == NULL)) 397 { 398 croute->listeners = NULL; 399 } 400 else if((prevlistener == NULL) && (listener->nextlistener != NULL)) 401 { 402 croute->listeners = listener->nextlistener; 403 result = 1; 404 } 405 else 406 { 407 prevlistener->nextlistener = listener->nextlistener; 408 result = 1; 409 } 410 411 free(listener); 412 return result; 413} 414 415#endif 416 417/** 418* Adds a specified route to the routingtable. 419* If the route already exists, the existing route 420* is updated... 421*/ 422#if (IGMP_IMMEDIATE_LEAVE) 423#ifdef USE_ATH_HEADER 424int insertRoute(uint32 port, uint32 group, int ifx, uint32 src) { 425#else 426int insertRoute(uint32 group, int ifx, uint32 src) { 427#endif 428#else 429int insertRoute(uint32 group, int ifx) { 430#endif 431 432 struct Config *conf = getCommonConfig(); 433 struct RouteTable* croute; 434 //int result = 1; 435#ifdef USE_ATH_HEADER 436 int i; 437#endif 438 // Sanitycheck the group adress... 439 if( ! IN_MULTICAST( ntohl(group) )) { 440 igmp_syslog(LOG_WARNING, 0, "The group address %s is not a valid Multicast group. Table insert failed.", 441 inetFmt(group, s1)); 442 return 0; 443 } 444 445 // Santiycheck the VIF index... 446 //if(ifx < 0 || ifx >= MAX_MC_VIFS) { 447 if(ifx >= MAX_MC_VIFS) { 448 igmp_syslog(LOG_WARNING, 0, "The VIF Ix %d is out of range (0-%d). Table insert failed.",ifx,MAX_MC_VIFS); 449 return 0; 450 } 451 452 // Try to find an existing route for this group... 453 croute = findRoute(group); 454 if(croute==NULL) { 455 struct RouteTable* newroute; 456 457 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "No existing route for %s. Create new.", 458 inetFmt(group, s1)); 459 460 461 // Create and initialize the new route table entry.. 462 newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable)); 463 // Insert the route desc and clear all pointers... 464 newroute->group = group; 465 newroute->originAddr = 0; 466 newroute->nextroute = NULL; 467 newroute->prevroute = NULL; 468 469 // The group is not joined initially. 470 newroute->upstrState = ROUTESTATE_NOTJOINED; 471 472 // The route is not active yet, so the age is unimportant. 473 newroute->ageValue = conf->robustnessValue; 474 newroute->ageActivity = 0; 475#ifdef USE_ATH_HEADER 476 for (i = 0; i <= PORT_MAX_NUM; i++) 477 newroute->port[i] = 0; 478 newroute->port[port]++; 479#endif 480#if (DNI_MULTI_LAN_SUPPORT)&&(SUPPORT_IGMPV3) 481 memset(&newroute->source, &ZEROSET, sizeof(ZEROSET)); 482 for (i = 0; i <= PORT_MAX_NUM; i++) 483 { 484 memcpy(&newroute->port_source[i], &ZEROSET, sizeof(ZEROSET)); 485 newroute->port_source[i].num = PORT_INIT_STATE; 486 newroute->port_mode[i] = st_is_in; 487 } 488 newroute->mode = 0; 489#endif 490#if (IGMP_IMMEDIATE_LEAVE) 491 newroute->listeners = NULL; 492 if(src != 0) 493 { 494 struct Listener* newlistener; 495 496 newlistener = (struct Listener*)malloc(sizeof(struct Listener)); 497 newlistener->nextlistener = NULL; 498 newlistener->srcAddr = src; 499 newroute->listeners = newlistener; 500#ifdef USE_ATH_HEADER 501 newlistener->port = port; 502#endif 503#if (DNI_MULTI_LAN_SUPPORT)&&(SUPPORT_IGMPV3) 504 newlistener->mode = st_is_ex; 505 newlistener->version = IGMP_V2_MEMBERSHIP_REPORT; 506 memcpy(&newlistener->source, &ZEROSET, sizeof(ZEROSET)); 507#endif 508 } 509#endif 510 511 BIT_ZERO(newroute->ageVifBits); // Initially we assume no listeners. 512 513 // Set the listener flag... 514 BIT_ZERO(newroute->vifBits); // Initially no listeners... 515 if(ifx >= 0) { 516 BIT_SET(newroute->vifBits, ifx); 517 } 518 519 // Check if there is a table already.... 520 if(routing_table == NULL) { 521 // No location set, so insert in on the table top. 522 routing_table = newroute; 523 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "No routes in table. Insert at beginning."); 524 } else { 525 526 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Found existing routes. Find insert location."); 527 528 // Check if the route could be inserted at the beginning... 529 if(routing_table->group > group) { 530 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Inserting at beginning, before route %s",inetFmt(routing_table->group,s1)); 531 532 // Insert at beginning... 533 newroute->nextroute = routing_table; 534 newroute->prevroute = NULL; 535 routing_table = newroute; 536 537 // If the route has a next node, the previous pointer must be updated. 538 if(newroute->nextroute != NULL) { 539 newroute->nextroute->prevroute = newroute; 540 } 541 542 } else { 543 544 // Find the location which is closest to the route. 545 for( croute = routing_table; croute->nextroute != NULL; croute = croute->nextroute ) { 546 // Find insert position. 547 if(croute->nextroute->group > group) { 548 break; 549 } 550 } 551 552 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Inserting after route %s",inetFmt(croute->group,s1)); 553 554 // Insert after current... 555 newroute->nextroute = croute->nextroute; 556 newroute->prevroute = croute; 557 if(croute->nextroute != NULL) { 558 croute->nextroute->prevroute = newroute; 559 } 560 croute->nextroute = newroute; 561 } 562 } 563 564 // Set the new route as the current... 565 croute = newroute; 566 567 // Log the cleanup in debugmode... 568 igmp_syslog(LOG_INFO, 0, "Inserted route table entry for %s on VIF #%d", 569 inetFmt(croute->group, s1),ifx); 570#ifdef USE_ATH_HEADER 571 if(commonConfig.mode&0x01){ 572 IF_DEBUG atlog(LOG_DEBUG, 0, 573 "Should insert group %s port %d to fdb table.", 574 inetFmt(group, s1),port); 575#if !(DNI_MULTI_LAN_SUPPORT) 576 if (port > 0) 577 insertFdb(group, port); 578#endif 579 } 580#endif 581 } else if(ifx >= 0) { 582 583 // The route exists already, so just update it. 584 BIT_SET(croute->vifBits, ifx); 585 586 // Register the VIF activity for the aging routine 587 BIT_SET(croute->ageVifBits, ifx); 588 589 // Log the cleanup in debugmode... 590 igmp_syslog(LOG_INFO, 0, "Updated route entry for %s on VIF #%d", 591 inetFmt(croute->group, s1), ifx); 592 593#if (IGMP_IMMEDIATE_LEAVE) 594 if(src != 0) 595 { 596#ifdef USE_ATH_HEADER 597 struct Listener *mbr; 598 if((mbr = findListener(croute, src)) == NULL) 599 { 600 insertListener(port, croute, src); 601 } 602 else 603 { 604#if !(DNI_MULTI_LAN_SUPPORT) 605 // if we found the client switch the port, try to update the snooping status. 606 if(mbr->port != port) 607 { 608 update_port(croute, mbr->port, port); 609 mbr->port = port; 610 } 611#endif 612 } 613#else 614 if(findListener(croute, src) == NULL) 615 { 616 insertListener(croute, src); 617 } 618 619#endif 620 update_listener(croute); 621 } 622#endif 623 624 // If the route is active, it must be reloaded into the Kernel.. 625 if(croute->originAddr != 0) { 626 // Update route in kernel... 627 if(!internUpdateKernelRoute(croute, 1)) { 628 igmp_syslog(LOG_WARNING, 0, "The insertion into Kernel failed."); 629 return 0; 630 } 631 } 632 } 633 634 // Send join message upstream, if the route has no joined flag... 635 if(croute->upstrState != ROUTESTATE_JOINED) { 636 // Send Join request upstream 637 sendJoinLeaveUpstream(croute, 1); 638 } 639 //send join report if there are listeners downstream in case some unfriendly video server provider periodically need IGMP report for giving video stream. 640 else if ( !strcmp(config_get("enable_bt_igmp"), "1") && croute->originAddr != 0 && croute->vifBits > 0) 641 { 642 struct IfDesc* upstrIf; 643 upstrIf = getIfByIx( upStreamVif ); 644 sendIgmp(upstrIf->InAdr.s_addr, all_group_igmpv3, IGMP_V3_MEMBERSHIP_REPORT, 0, group, sizeof(struct igmpv3_grec)); 645 //sendIgmp(upstrIf->InAdr.s_addr, allrouters_group, IGMP_V2_MEMBERSHIP_REPORT, 0, group, 0); // send IGMPV2 join report 646 } 647 648 IF_DEBUG logRouteTable("Insert Route"); 649 650 return 1; 651} 652 653/** 654* Activates a passive group. If the group is already 655* activated, it's reinstalled in the kernel. If 656* the route is activated, no originAddr is needed. 657*/ 658int activateRoute(uint32 group, uint32 originAddr) { 659 struct RouteTable* croute; 660 int result = 0; 661 662 // Find the requested route. 663 croute = findRoute(group); 664 if(croute == NULL) { 665 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "No table entry for %s [From: %s]. Inserting route.", 666 inetFmt(group, s1),inetFmt(originAddr, s2)); 667 668 // Insert route, but no interfaces have yet requested it downstream. 669#if (IGMP_IMMEDIATE_LEAVE) 670#ifdef USE_ATH_HEADER 671 insertRoute(0, group, -1, 0); 672#else 673 insertRoute(group, -1, 0); 674#endif 675#else 676 insertRoute(group, -1); 677#endif 678 // Retrieve the route from table... 679 croute = findRoute(group); 680 } 681 682 if(croute != NULL) { 683 // If the origin address is set, update the route data. 684 if(originAddr > 0) { 685 if(croute->originAddr > 0 && croute->originAddr!=originAddr) { 686 igmp_syslog(LOG_WARNING, 0, "The origin for route %s changed from %s to %s", 687 inetFmt(croute->group, s1), 688 inetFmt(croute->originAddr, s2), 689 inetFmt(originAddr, s3)); 690 } 691 croute->originAddr = originAddr; 692 } 693 694 // Only update kernel table if there are listeners ! 695 if(croute->vifBits > 0) { 696 result = internUpdateKernelRoute(croute, 1); 697 } 698 } 699 IF_DEBUG logRouteTable("Activate Route"); 700 701 return result; 702} 703 704 705/** 706* This function loops through all routes, and updates the age 707* of any active routes. 708*/ 709void ageActiveRoutes() { 710 struct RouteTable *croute, *nroute; 711 712 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Aging routes in table."); 713 714 // Scan all routes... 715 for( croute = routing_table; croute != NULL; croute = nroute ) { 716 717 // Keep the next route (since current route may be removed)... 718 nroute = croute->nextroute; 719 // Run the aging round algorithm. 720 if(croute->upstrState != ROUTESTATE_CHECK_LAST_MEMBER) { 721 // Only age routes if Last member probe is not active... 722 internAgeRoute(croute); 723 } 724 } 725 IF_DEBUG logRouteTable("Age active routes"); 726} 727 728/** 729* Should be called when a leave message is recieved, to 730* mark a route for the last member probe state. 731*/ 732#if (IGMP_IMMEDIATE_LEAVE) 733void setRouteLastMemberMode(uint32 group, uint32 src) { 734#else 735void setRouteLastMemberMode(uint32 group) { 736#endif 737 struct Config *conf = getCommonConfig(); 738 struct RouteTable *croute; 739 740 croute = findRoute(group); 741 if(croute!=NULL) { 742 // Check for fast leave mode... 743 if(croute->upstrState == ROUTESTATE_JOINED && conf->fastUpstreamLeave) { 744#if (IGMP_IMMEDIATE_LEAVE) 745 if( removeListener(croute, src) == 0 ) 746 { 747 BIT_ZERO(croute->ageVifBits); 748 749 // No activity was registered within the timelimit, so remove the route. 750 removeRoute(croute); 751 } 752 else 753 { 754#if (DNI_MULTI_LAN_SUPPORT) 755 update_all_ports(croute); 756#endif 757 if(croute->listeners == NULL) 758 { 759 // Send a leave message right away.. 760 sendJoinLeaveUpstream(croute, 0); 761 } 762 } 763#else 764 // Send a leave message right away.. 765 sendJoinLeaveUpstream(croute, 0); 766#endif 767#if (SUPPURT_MCAST_TO_UNICAST) 768 leave_group(src, group); 769#endif 770 } 771 // Set the routingstate to Last member check... 772#if (!IGMP_IMMEDIATE_LEAVE) 773 croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER; 774 // Set the count value for expiring... (-1 since first aging) 775 croute->ageValue = conf->lastMemberQueryCount; 776#endif 777 778 } 779} 780 781 782/** 783* Ages groups in the last member check state. If the 784* route is not found, or not in this state, 0 is returned. 785*/ 786int lastMemberGroupAge(uint32 group) { 787 struct Config *conf = NULL; 788 struct RouteTable *croute; 789 790 conf = getCommonConfig(); 791 792 croute = findRoute(group); 793 if(croute!=NULL) { 794 if(croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER) { 795 return !internAgeRoute(croute); 796 } else { 797 return 0; 798 } 799 } 800 return 0; 801} 802 803/** 804* Remove a specified route. Returns 1 on success, 805* and 0 if route was not found. 806*/ 807int removeRoute(struct RouteTable* croute) { 808 struct Config *conf = getCommonConfig(); 809 int result = 1; 810 811 // If croute is null, no routes was found. 812 if(croute==NULL) { 813 return 0; 814 } 815#if (IGMP_IMMEDIATE_LEAVE) 816 if (croute->listeners) 817 { 818 struct Listener *listener, *nextlistener; 819 DPRINTF("%s :: to remove entry [%x]\n", __FUNCTION__, croute->group); 820 listener = croute->listeners; 821 nextlistener = listener->nextlistener; 822 while (nextlistener) 823 { 824#if (SUPPURT_MCAST_TO_UNICAST) 825 leave_group(listener->srcAddr, croute->group); 826#endif 827 free(listener); 828 listener = nextlistener; 829 nextlistener = listener->nextlistener; 830 } 831#if (SUPPURT_MCAST_TO_UNICAST) 832 leave_group(listener->srcAddr, croute->group); 833#endif 834 free(listener); 835 croute->listeners = NULL; 836 } 837#if (DNI_MULTI_LAN_SUPPORT) 838 update_all_ports(croute); 839#endif 840#endif 841 // Log the cleanup in debugmode... 842 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Removed route entry for %s from table.", 843 inetFmt(croute->group, s1)); 844 845 //BIT_ZERO(croute->vifBits); 846 847 // Uninstall current route from kernel 848 if(!internUpdateKernelRoute(croute, 0)) { 849 igmp_syslog(LOG_WARNING, 0, "The removal from Kernel failed."); 850 result = 0; 851 } 852 853 // Send Leave request upstream if group is joined 854 if(croute->upstrState == ROUTESTATE_JOINED || 855 (croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER && !conf->fastUpstreamLeave)) 856 { 857 sendJoinLeaveUpstream(croute, 0); 858 } 859 860 // Update pointers... 861 if(croute->prevroute == NULL) { 862 // Topmost node... 863 if(croute->nextroute != NULL) { 864 croute->nextroute->prevroute = NULL; 865 } 866 routing_table = croute->nextroute; 867 868 } else { 869 croute->prevroute->nextroute = croute->nextroute; 870 if(croute->nextroute != NULL) { 871 croute->nextroute->prevroute = croute->prevroute; 872 } 873 } 874 // Free the memory, and set the route to NULL... 875 free(croute); 876 croute = NULL; 877 878 IF_DEBUG logRouteTable("Remove route"); 879 880 return result; 881} 882 883 884/** 885* Ages a specific route 886*/ 887int internAgeRoute(struct RouteTable* croute) { 888 struct Config *conf = getCommonConfig(); 889 int result = 0; 890 891 // Drop age by 1. 892 croute->ageValue--; 893 // Check if there has been any activity... 894 if( croute->ageVifBits > 0 && croute->ageActivity == 0 ) { 895 // There was some activity, check if all registered vifs responded. 896 if(croute->vifBits == croute->ageVifBits) { 897 // Everything is in perfect order, so we just update the route age. 898 croute->ageValue = conf->robustnessValue; 899 //croute->ageActivity = 0; 900 } else { 901 // One or more VIF has not gotten any response. 902 croute->ageActivity++; 903 // Update the actual bits for the route... 904 croute->vifBits = croute->ageVifBits; 905 } 906 } 907 // Check if there have been activity in aging process... 908 else if( croute->ageActivity > 0 ) { 909 // If the bits are different in this round, we must 910 if(croute->vifBits != croute->ageVifBits) { 911 // Or the bits together to insure we don't lose any listeners. 912 croute->vifBits |= croute->ageVifBits; 913 914 // Register changes in this round as well.. 915 croute->ageActivity++; 916 } 917 } 918 919 // If the aging counter has reached zero, its time for updating... 920 if(croute->ageValue == 0) { 921 // Check for activity in the aging process, 922 if(croute->ageActivity>0) { 923 924 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Updating route after aging : %s", 925 inetFmt(croute->group,s1)); 926 927 // Just update the routing settings in kernel... 928 internUpdateKernelRoute(croute, 1); 929 930 // We append the activity counter to the age, and continue... 931 croute->ageValue = croute->ageActivity; 932 croute->ageActivity = 0; 933 } else { 934 935 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Removing group %s. Died of old age.", 936 inetFmt(croute->group,s1)); 937 938 // No activity was registered within the timelimit, so remove the route. 939 removeRoute(croute); 940 } 941 // Tell that the route was updated... 942 result = 1; 943 } 944 // The aging vif bits must be reset for each round... 945 BIT_ZERO(croute->ageVifBits); 946 947 return result; 948} 949 950/** 951* Updates the Kernel routing table. If activate is 1, the route 952* is (re-)activated. If activate is false, the route is removed. 953*/ 954int internUpdateKernelRoute(struct RouteTable *route, int activate) { 955 struct MRouteDesc mrDesc; 956 struct IfDesc *Dp; 957 unsigned Ix; 958 959 if(route->originAddr>0) { 960 961 // Build route descriptor from table entry... 962 // Set the source address and group address... 963 mrDesc.McAdr.s_addr = route->group; 964 mrDesc.OriginAdr.s_addr = route->originAddr; 965 // clear output interfaces 966 memset( mrDesc.TtlVc, 0, sizeof( mrDesc.TtlVc ) ); 967 968 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Vif bits : 0x%08x", route->vifBits); 969 970 // Set the TTL's for the route descriptor... 971 for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) { 972 if(Dp->state == IF_STATE_UPSTREAM) { 973 //IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Identified VIF #%d as upstream.", Dp->index); 974 mrDesc.InVif = Dp->index; 975 } 976 else if(BIT_TST(route->vifBits, Dp->index)) { 977 IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Setting TTL for Vif %d to %d", Dp->index, Dp->threshold); 978 mrDesc.TtlVc[ Dp->index ] = Dp->threshold; 979 } 980 } 981 982 // Do the actual Kernel route update... 983 if(activate) { 984 // Add route in kernel... 985 addMRoute( &mrDesc ); 986 } else { 987 // Delete the route from Kernel... 988#ifndef USE_ATH_HEADER 989 mrDesc.OriginAdr.s_addr = 0; 990#endif 991 delMRoute( &mrDesc ); 992 } 993 994 } else { 995 igmp_syslog(LOG_NOTICE, 0, "Route is not active. No kernel updates done."); 996 } 997 998 return 1; 999} 1000 1001/** 1002* Debug function that writes the routing table entries 1003* to the igmp_syslog. 1004*/ 1005void logRouteTable(char *header) { 1006 IF_DEBUG { 1007 struct RouteTable* croute = routing_table; 1008 unsigned rcount = 0; 1009 1010 igmp_syslog(LOG_DEBUG, 0, "\nCurrent routing table (%s);\n-----------------------------------------------------\n", header); 1011 if(croute==NULL) { 1012 igmp_syslog(LOG_DEBUG, 0, "No routes in table..."); 1013 } else { 1014 do { 1015 /* 1016 igmp_syslog(LOG_DEBUG, 0, "#%d: Src: %s, Dst: %s, Age:%d, St: %s, Prev: 0x%08x, T: 0x%08x, Next: 0x%08x", 1017 rcount, inetFmt(croute->originAddr, s1), inetFmt(croute->group, s2), 1018 croute->ageValue,(croute->originAddr>0?"A":"I"), 1019 croute->prevroute, croute, croute->nextroute); 1020 */ 1021 igmp_syslog(LOG_DEBUG, 0, "#%d: Src: %s, Dst: %s, Age:%d, St: %s, OutVifs: 0x%08x", 1022 rcount, inetFmt(croute->originAddr, s1), inetFmt(croute->group, s2), 1023 croute->ageValue,(croute->originAddr>0?"A":"I"), 1024 croute->vifBits); 1025 1026 croute = croute->nextroute; 1027 1028 rcount++; 1029 } while ( croute != NULL ); 1030 } 1031 1032 igmp_syslog(LOG_DEBUG, 0, "\n-----------------------------------------------------\n"); 1033 } 1034} 1035