/** * Activates a passive group. If the group is already * activated, it's reinstalled in the kernel. If * the route is activated, no originAddr is needed. */ int activateRoute(uint32_t group, uint32_t originAddr) { struct RouteTable* croute; int result = 0; // Find the requested route. croute = findRoute(group); if(croute == NULL) { my_log(LOG_DEBUG, 0, "No table entry for %s [From: %s]. Inserting route.", inetFmt(group, s1),inetFmt(originAddr, s2)); // Insert route, but no interfaces have yet requested it downstream. insertRoute(group, -1); // Retrieve the route from table... croute = findRoute(group); } if(croute != NULL) { // If the origin address is set, update the route data. if(originAddr > 0) { // find this origin, or an unused slot int i; for (i = 0; i < MAX_ORIGINS; i++) { // unused slots are at the bottom, so we can't miss this origin if (croute->originAddrs[i] == originAddr || croute->originAddrs[i] == 0) { break; } } if (i == MAX_ORIGINS) { i = MAX_ORIGINS - 1; my_log(LOG_WARNING, 0, "Too many origins for route %s; replacing %s with %s", inetFmt(croute->group, s1), inetFmt(croute->originAddrs[i], s2), inetFmt(originAddr, s3)); } // set origin croute->originAddrs[i] = originAddr; // move it to the top while (i > 0) { uint32_t t = croute->originAddrs[i - 1]; croute->originAddrs[i - 1] = croute->originAddrs[i]; croute->originAddrs[i] = t; i--; } } // Only update kernel table if there are listeners ! if(croute->vifBits > 0) { result = internUpdateKernelRoute(croute, 1); } } logRouteTable("Activate Route"); return result; }
/** * Sends a group specific member report query until the * group times out... */ static 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); my_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); }
/** * Internal function to send join or leave requests for * a specified route upstream... */ void sendJoinLeaveUpstream(struct RouteTable* route, int join) { struct IfDesc* upstrIf; // Get the upstream VIF... upstrIf = getIfByIx( upStreamVif ); if(upstrIf == NULL) { my_log(LOG_ERR, 0 ,"FATAL: Unable to get Upstream IF."); } // Check if there is a white list for the upstram VIF if (upstrIf->allowedgroups != NULL) { uint32_t group = route->group; struct SubnetList* sn; // Check if this Request is legit to be forwarded to upstream for(sn = upstrIf->allowedgroups; sn != NULL; sn = sn->next) if((group & sn->subnet_mask) == sn->subnet_addr) // Forward is OK... break; if (sn == NULL) { my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(group, s1)); return; } } // Send join or leave request... if(join) { // Only join a group if there are listeners downstream... if(route->vifBits > 0) { my_log(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s", inetFmt(route->group, s1), inetFmt(upstrIf->InAdr.s_addr, s2)); //k_join(route->group, upstrIf->InAdr.s_addr); joinMcGroup( getMcGroupSock(), upstrIf, route->group ); route->upstrState = ROUTESTATE_JOINED; } else { my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", inetFmt(route->group, s1)); } } else { // Only leave if group is not left already... if(route->upstrState != ROUTESTATE_NOTJOINED) { my_log(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s", inetFmt(route->group, s1), inetFmt(upstrIf->InAdr.s_addr, s2)); //k_leave(route->group, upstrIf->InAdr.s_addr); leaveMcGroup( getMcGroupSock(), upstrIf, route->group ); route->upstrState = ROUTESTATE_NOTJOINED; } } }
/** * Handles incoming membership reports, and * appends them to the routing table. */ void acceptGroupReport(uint32_t src, uint32_t group, uint8_t type) { 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 recieved. 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) { #ifdef RALINK_ESW_SUPPORT insert_multicast_ip(ntohl(group), ntohl(src)); #endif 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) { #ifdef RALINK_ESW_SUPPORT insert_multicast_ip(ntohl(group), ntohl(src)); #endif // 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 recieved on. my_log(LOG_INFO, 0, "Mebership report was recieved on %s. Ignoring.", sourceVif->state==IF_STATE_UPSTREAM?"the upstream interface":"a disabled interface"); } }
/** * Clear all routes from routing table, and alerts Leaves upstream. */ void clearAllRoutes() { struct RouteTable *croute, *remainroute; // Loop through all routes... for(croute = routing_table; croute; croute = remainroute) { remainroute = croute->nextroute; // Log the cleanup in debugmode... my_log(LOG_DEBUG, 0, "Removing route entry for %s", inetFmt(croute->group, s1)); // Uninstall current route if(!internUpdateKernelRoute(croute, 0)) { my_log(LOG_WARNING, 0, "The removal from Kernel failed."); } // Send Leave message upstream. sendJoinLeaveUpstream(croute, 0); // Clear memory, and set pointer to next route... free(croute); } routing_table = NULL; // Send a notice that the routing table is empty... my_log(LOG_NOTICE, 0, "All routes removed. Routing table is empty."); }
/** * 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 recieved. 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); #ifdef RALINK_ESW_SUPPORT remove_member(ntohl(group), ntohl(src)); #endif } 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)); } }
void k_set_if(uint32_t ifa) { struct in_addr adr; adr.s_addr = ifa; if (setsockopt(MRouterFD, IPPROTO_IP, IP_MULTICAST_IF, (char *)&adr, sizeof(adr)) < 0) my_log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s", inetFmt(ifa, s1)); }
/* * Call build_igmp() to build an IGMP message in the output packet buffer. * Then send the message from the interface with IP address 'src' to * destination 'dst'. */ void sendIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) { struct sockaddr_in sdst; int setloop = 0, setigmpsource = 0; buildIgmp(src, dst, type, code, group, datalen); if (IN_MULTICAST(ntohl(dst))) { k_set_if(src); setigmpsource = 1; if (type != IGMP_DVMRP || dst == allhosts_group) { setloop = 1; k_set_loop(true); } } memset(&sdst, 0, sizeof(sdst)); sdst.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sdst.sin_len = sizeof(sdst); #endif sdst.sin_addr.s_addr = dst; if (sendto(MRouterFD, send_buf, IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { if (errno == ENETDOWN) my_log(LOG_ERR, errno, "Sender VIF was down."); else my_log(LOG_INFO, errno, "sendto to %s on %s", inetFmt(dst, s1), inetFmt(src, s2)); } if(setigmpsource) { if (setloop) { k_set_loop(false); } // Restore original... k_set_if(INADDR_ANY); } my_log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", igmpPacketKind(type, code), src == INADDR_ANY ? "INADDR_ANY" : inetFmt(src, s1), inetFmt(dst, s2)); }
/** * Sends a general membership query on downstream VIFs */ void sendGeneralMembershipQuery() { struct Config *conf = getCommonConfig(); struct IfDesc *Dp; unsigned Ix; #ifdef RALINK_ESW_SUPPORT clear_all_entries_report(); #endif // 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, 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); } }
/** * Handles incoming membership reports, and * appends them to the routing table. */ void acceptGroupReport(uint32_t src, uint32_t group, uint8_t type) { 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 recieved. 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); // 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. my_log(LOG_INFO, 0, "Membership report was recieved on %s. Ignoring.", sourceVif->state==IF_STATE_UPSTREAM?"the upstream interface":"a disabled interface"); } }
/** * Remove a specified route. Returns 1 on success, * and 0 if route was not found. */ int removeRoute(struct RouteTable* croute) { struct Config *conf = getCommonConfig(); int result = 1; // If croute is null, no routes was found. if(croute==NULL) { return 0; } // Log the cleanup in debugmode... my_log(LOG_DEBUG, 0, "Removed route entry for %s from table.", inetFmt(croute->group, s1)); //BIT_ZERO(croute->vifBits); // Uninstall current route from kernel if(!internUpdateKernelRoute(croute, 0)) { my_log(LOG_WARNING, 0, "The removal from Kernel failed."); result = 0; } // Send Leave request upstream if group is joined if(croute->upstrState == ROUTESTATE_JOINED || (croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER && !conf->fastUpstreamLeave)) { sendJoinLeaveUpstream(croute, 0); } // Update pointers... if(croute->prevroute == NULL) { // Topmost node... if(croute->nextroute != NULL) { croute->nextroute->prevroute = NULL; } routing_table = croute->nextroute; } else { croute->prevroute->nextroute = croute->nextroute; if(croute->nextroute != NULL) { croute->nextroute->prevroute = croute->prevroute; } } // Free the memory, and set the route to NULL... free(croute); croute = NULL; logRouteTable("Remove route"); return result; }
/** * Initializes the routing table. */ void initRouteTable() { unsigned Ix; struct IfDesc *Dp; // Clear routing table... routing_table = NULL; // Join the all routers group on downstream vifs... for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { // If this is a downstream vif, we should join the All routers group... if( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state == IF_STATE_DOWNSTREAM) { my_log(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s", inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); //k_join(allrouters_group, Dp->InAdr.s_addr); joinMcGroup( getMcGroupSock(), Dp, allrouters_group ); my_log(LOG_DEBUG, 0, "Joining all igmpv3 multicast routers group %s on vif %s", inetFmt(alligmp3_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); joinMcGroup( getMcGroupSock(), Dp, alligmp3_group ); } } }
/** * Debug function that writes the routing table entries * to the log. */ void logRouteTable(char *header) { struct RouteTable* croute = routing_table; unsigned rcount = 0; int i; my_log(LOG_DEBUG, 0, ""); my_log(LOG_DEBUG, 0, "Current routing table (%s):", header); my_log(LOG_DEBUG, 0, "-----------------------------------------------------"); if(croute==NULL) { my_log(LOG_DEBUG, 0, "No routes in table..."); } else { do { char st = 'I'; char src[MAX_ORIGINS * 30 + 1]; src[0] = '\0'; for (i = 0; i < MAX_ORIGINS; i++) { if (croute->originAddrs[i] == 0) { continue; } st = 'A'; sprintf(src + strlen(src), "Src%d: %s, ", i, inetFmt(croute->originAddrs[i], s1)); } my_log(LOG_DEBUG, 0, "#%d: %sDst: %s, Age:%d, St: %c, OutVifs: 0x%08x", rcount, src, inetFmt(croute->group, s2), croute->ageValue, st, croute->vifBits); croute = croute->nextroute; rcount++; } while ( croute != NULL ); } my_log(LOG_DEBUG, 0, "-----------------------------------------------------"); }
/** * Common function for joining or leaving a MCast group. */ int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ) { struct ip_mreq CtlReq; const char *CmdSt = Cmd == 'j' ? "join" : "leave"; memset(&CtlReq, 0, sizeof(CtlReq)); CtlReq.imr_multiaddr.s_addr = mcastaddr; CtlReq.imr_interface.s_addr = IfDp->InAdr.s_addr; { my_log( LOG_NOTICE, 0, "%sMcGroup: %s on %s", CmdSt, inetFmt( mcastaddr, s1 ), IfDp ? IfDp->Name : "<any>" ); } if( setsockopt( UdpSock, IPPROTO_IP, Cmd == 'j' ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (void *)&CtlReq, sizeof( CtlReq ) ) ) { my_log( LOG_WARNING, errno, "MRT_%s_MEMBERSHIP failed", Cmd == 'j' ? "ADD" : "DROP" ); return 1; } return 0; }
/** * Process a newly received IGMP packet that is sitting in the input * packet buffer. */ void acceptIgmp(int recvlen) { register uint32_t src, dst, group; struct ip *ip; struct igmp *igmp; struct igmpv3_report *igmpv3; struct igmpv3_grec *grec; int ipdatalen, iphdrlen, ngrec, nsrcs; if (recvlen < sizeof(struct ip)) { my_log(LOG_WARNING, 0, "received packet too short (%u bytes) for IP header", recvlen); return; } ip = (struct ip *)recv_buf; src = ip->ip_src.s_addr; dst = ip->ip_dst.s_addr; /* filter local multicast 239.255.255.250 */ if (dst == htonl(0xEFFFFFFA)) { my_log(LOG_NOTICE, 0, "The IGMP message was local multicast. Ignoring."); return; } /* * this is most likely a message from the kernel indicating that * a new src grp pair message has arrived and so, it would be * necessary to install a route into the kernel for this. */ if (ip->ip_p == 0) { if (src == 0 || dst == 0) { my_log(LOG_WARNING, 0, "kernel request not accurate"); } else { struct IfDesc *checkVIF; // Check if the source address matches a valid address on upstream vif. checkVIF = getIfByIx( upStreamVif ); if(checkVIF == 0) { my_log(LOG_ERR, 0, "Upstream VIF was null."); return; } else if(src == checkVIF->InAdr.s_addr) { my_log(LOG_NOTICE, 0, "Route activation request from %s for %s is from myself. Ignoring.", inetFmt(src, s1), inetFmt(dst, s2)); return; } else if(!isAdressValidForIf(checkVIF, src)) { my_log(LOG_WARNING, 0, "The source address %s for group %s, is not in any valid net for upstream VIF.", inetFmt(src, s1), inetFmt(dst, s2)); return; } // Activate the route. my_log(LOG_DEBUG, 0, "Route activate request from %s to %s", inetFmt(src,s1), inetFmt(dst,s2)); activateRoute(dst, src); } return; } iphdrlen = ip->ip_hl << 2; ipdatalen = ip_data_len(ip); if (iphdrlen + ipdatalen != recvlen) { my_log(LOG_WARNING, 0, "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", inetFmt(src, s1), recvlen, iphdrlen, ipdatalen); return; } igmp = (struct igmp *)(recv_buf + iphdrlen); if ((ipdatalen < IGMP_MINLEN) || (igmp->igmp_type == IGMP_V3_MEMBERSHIP_REPORT && ipdatalen <= IGMPV3_MINLEN)) { my_log(LOG_WARNING, 0, "received IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inetFmt(src, s1)); return; } my_log(LOG_NOTICE, 0, "RECV %s from %-15s to %s", igmpPacketKind(igmp->igmp_type, igmp->igmp_code), inetFmt(src, s1), inetFmt(dst, s2) ); switch (igmp->igmp_type) { case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: group = igmp->igmp_group.s_addr; acceptGroupReport(src, group, igmp->igmp_type); return; case IGMP_V3_MEMBERSHIP_REPORT: igmpv3 = (struct igmpv3_report *)(recv_buf + iphdrlen); grec = &igmpv3->igmp_grec[0]; ngrec = ntohs(igmpv3->igmp_ngrec); while (ngrec--) { if ((uint8_t *)igmpv3 + ipdatalen < (uint8_t *)grec + sizeof(*grec)) break; group = grec->grec_mca.s_addr; nsrcs = ntohs(grec->grec_nsrcs); switch (grec->grec_type) { case IGMPV3_MODE_IS_INCLUDE: case IGMPV3_CHANGE_TO_INCLUDE: if (nsrcs == 0) { acceptLeaveMessage(src, group); break; } /* else fall through */ case IGMPV3_MODE_IS_EXCLUDE: case IGMPV3_CHANGE_TO_EXCLUDE: case IGMPV3_ALLOW_NEW_SOURCES: acceptGroupReport(src, group, IGMP_V2_MEMBERSHIP_REPORT); break; case IGMPV3_BLOCK_OLD_SOURCES: break; default: my_log(LOG_INFO, 0, "ignoring unknown IGMPv3 group record type %x from %s to %s for %s", grec->grec_type, inetFmt(src, s1), inetFmt(dst, s2), inetFmt(group, s3)); break; } grec = (struct igmpv3_grec *) (&grec->grec_src[nsrcs] + grec->grec_auxwords * 4); } return; case IGMP_V2_LEAVE_GROUP: group = igmp->igmp_group.s_addr; acceptLeaveMessage(src, group); return; case IGMP_MEMBERSHIP_QUERY: return; default: my_log(LOG_INFO, 0, "ignoring unknown IGMP message type %x from %s to %s", igmp->igmp_type, inetFmt(src, s1), inetFmt(dst, s2)); return; } }
/** * Accept a V3 Membership Report */ void acceptV3GroupReport(uint32_t src, uint8_t type, struct igmpv3_groupreport* igmpv3rep) { 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 recieved. 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) { // Log the state of the interface the report was recieved on. my_log(LOG_INFO, 0, "Membership report was recieved on %s. Ignoring.", sourceVif->state==IF_STATE_UPSTREAM?"the upstream interface":"a disabled interface"); } else { my_log(LOG_DEBUG, 0, "IGMPv3 Membership Report") // TODO: Can we batch-report here? uint16_t num_entries = ntohl(igmpv3rep->num_entries); uint16_t eix = 0; while ( eix < num_entries ) { struct igmpv3_groupreport_entry* e = (struct igmpv3_groupreport_entry* igmpv3rep->grec) + eix; uint16_t num_sources = ntohl(e->nsrcs); uint16_t six = 0; while ( six < num_sources ) { uint32_t* srcgroup = (uint32_t* e->sources) + six; group = ntohl(*srcgroup); switch ( e->type == 0x0 ) { case IGMPV3_GRE_MODE_INCLUDE: case IGMPV3_GRE_CHMODE_INCLUDE: case IGMPV3_GRE_ALLOW_NEW_SOURCES: my_log(LOG_DEBUG, 0, "IGMPv3 Subscribe to %s.", inetFmt(group, s1)); insertRoute(group, sourceVif->index); break; case IGMPV3_GRE_MODE_EXCLUDE: case IGMPV3_GRE_CHMODE_EXCLUDE: case IGMPV3_GRE_BLOCK_OLD_SOURCES: my_log(LOG_DEBUG, 0, "IGMPv3 Unsubscribe from %s.", inetFmt(group, s1)); deleteRoute(group, sourceVif->index); break; default: my_log(LOG_INFO, 0, "Membership V3 group report received with unknown type %d.", e->type); } }; eix++; }; return; } }
/** * Ages a specific route */ int internAgeRoute(struct RouteTable* croute) { struct Config *conf = getCommonConfig(); int result = 0; // Drop age by 1. croute->ageValue--; // Check if there has been any activity... if( croute->ageVifBits > 0 && croute->ageActivity == 0 ) { // There was some activity, check if all registered vifs responded. if(croute->vifBits == croute->ageVifBits) { // Everything is in perfect order, so we just update the route age. croute->ageValue = conf->robustnessValue; //croute->ageActivity = 0; } else { // One or more VIF has not gotten any response. croute->ageActivity++; // Update the actual bits for the route... croute->vifBits = croute->ageVifBits; } } // Check if there have been activity in aging process... else if( croute->ageActivity > 0 ) { // If the bits are different in this round, we must if(croute->vifBits != croute->ageVifBits) { // Or the bits together to insure we don't lose any listeners. croute->vifBits |= croute->ageVifBits; // Register changes in this round as well.. croute->ageActivity++; } } // If the aging counter has reached zero, its time for updating... if(croute->ageValue == 0) { // Check for activity in the aging process, if(croute->ageActivity>0) { my_log(LOG_DEBUG, 0, "Updating route after aging : %s", inetFmt(croute->group,s1)); // Just update the routing settings in kernel... internUpdateKernelRoute(croute, 1); // We append the activity counter to the age, and continue... croute->ageValue = croute->ageActivity; croute->ageActivity = 0; } else { my_log(LOG_DEBUG, 0, "Removing group %s. Died of old age.", inetFmt(croute->group,s1)); // No activity was registered within the timelimit, so remove the route. removeRoute(croute); } // Tell that the route was updated... result = 1; } // The aging vif bits must be reset for each round... BIT_ZERO(croute->ageVifBits); return result; }
/** * Process a newly received IGMP packet that is sitting in the input * packet buffer. */ void acceptIgmp(int recvlen) { register uint32_t src, dst, group; struct ip *ip; struct igmp *igmp; int ipdatalen, iphdrlen, igmpdatalen; if (recvlen < sizeof(struct ip)) { my_log(LOG_WARNING, 0, "received packet too short (%u bytes) for IP header", recvlen); return; } ip = (struct ip *)recv_buf; src = ip->ip_src.s_addr; dst = ip->ip_dst.s_addr; /* * this is most likely a message from the kernel indicating that * a new src grp pair message has arrived and so, it would be * necessary to install a route into the kernel for this. */ if (ip->ip_p == 0) { if (src == 0 || dst == 0) { my_log(LOG_WARNING, 0, "kernel request not accurate"); } else { struct IfDesc *checkVIF; // Check if the source address matches a valid address on upstream vif. checkVIF = getIfByIx( upStreamVif ); if(checkVIF == 0) { my_log(LOG_ERR, 0, "Upstream VIF was null."); return; } else if(src == checkVIF->InAdr.s_addr) { my_log(LOG_NOTICE, 0, "Route activation request from %s for %s is from myself. Ignoring.", inetFmt(src, s1), inetFmt(dst, s2)); return; } /* else if(!isAdressValidForIf(checkVIF, src)) { my_log(LOG_WARNING, 0, "The source address %s for group %s, is not in any valid net for upstream VIF.", inetFmt(src, s1), inetFmt(dst, s2)); return; }*/ // Activate the route. my_log(LOG_DEBUG, 0, "Route activate request from %s to %s", inetFmt(src,s1), inetFmt(dst,s2)); activateRoute(dst, src); } return; } iphdrlen = ip->ip_hl << 2; ipdatalen = ip_data_len(ip); if (iphdrlen + ipdatalen != recvlen) { my_log(LOG_WARNING, 0, "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", inetFmt(src, s1), recvlen, iphdrlen, ipdatalen); return; } igmp = (struct igmp *)(recv_buf + iphdrlen); group = igmp->igmp_group.s_addr; igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { my_log(LOG_WARNING, 0, "received IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inetFmt(src, s1)); return; } my_log(LOG_NOTICE, 0, "RECV %s from %-15s to %s", igmpPacketKind(igmp->igmp_type, igmp->igmp_code), inetFmt(src, s1), inetFmt(dst, s2) ); switch (igmp->igmp_type) { case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: acceptGroupReport(src, group, igmp->igmp_type); return; case IGMP_V2_LEAVE_GROUP: acceptLeaveMessage(src, group); return; case IGMP_MEMBERSHIP_QUERY: return; default: my_log(LOG_INFO, 0, "ignoring unknown IGMP message type %x from %s to %s", igmp->igmp_type, inetFmt(src, s1), inetFmt(dst, s2)); return; } }
/** * Process a newly received IGMP packet that is sitting in the input * packet buffer. */ void acceptIgmp(int recvlen) { register uint32 src, dst, group; struct ip *ip; struct igmp *igmp; int ipdatalen, iphdrlen, igmpdatalen; if (recvlen < sizeof(struct ip)) { log_msg(LOG_WARNING, 0, "received packet too short (%u bytes) for IP header", recvlen); return; } ip = (struct ip *)recv_buf; src = ip->ip_src.s_addr; dst = ip->ip_dst.s_addr; //IF_DEBUG log_msg(LOG_DEBUG, 0, "Got a IGMP request to process..."); /* * this is most likely a message from the kernel indicating that * a new src grp pair message has arrived and so, it would be * necessary to install a route into the kernel for this. */ if (ip->ip_p == 0) { if (src == 0 || dst == 0) { log_msg(LOG_WARNING, 0, "kernel request not accurate"); } else { #if 0 /*remove for cd-router test wan remoteHostIp is different subnet with wan mask*/ struct IfDesc *checkVIF; // Check if the source address matches a valid address on upstream vif. checkVIF = getIfByIx( upStreamVif ); if(checkVIF == 0) { log_msg(LOG_ERR, 0, "Upstream VIF was null."); return; } else if(src == checkVIF->InAdr.s_addr) { log_msg(LOG_NOTICE, 0, "Route activation request from %s for %s is from myself. Ignoring.", inetFmt(src, s1), inetFmt(dst, s2)); return; } else if(!isAdressValidForIf(checkVIF, src)) { log_msg(LOG_WARNING, 0, "The source address %s for group %s, is not in any valid net for upstream VIF.", inetFmt(src, s1), inetFmt(dst, s2)); return; } #else if ((dst & LOCAL_PRIVATE_NETMASK) == LOCAL_PRIVATE_NETMASK){ IF_DEBUG log_msg(LOG_DEBUG, 0, "Got private dst multicast address %s passthrough return ", inetFmt(dst,s2)); return; } #endif // Activate the route. IF_DEBUG log_msg(LOG_DEBUG, 0, "Route activate request from %s to %s", inetFmt(src,s1), inetFmt(dst,s2)); activateRoute(dst, src); } return; } iphdrlen = ip->ip_hl << 2; ipdatalen = ntohs(ip->ip_len) - iphdrlen; if (iphdrlen + ipdatalen != recvlen) { log_msg(LOG_WARNING, 0, "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", inetFmt(src, s1), recvlen, iphdrlen, ipdatalen); return; } igmp = (struct igmp *)(recv_buf + iphdrlen); group = igmp->igmp_group.s_addr; igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { log_msg(LOG_WARNING, 0, "received IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inetFmt(src, s1)); return; } log_msg(LOG_NOTICE, 0, "RECV %s from %-15s to %s", igmpPacketKind(igmp->igmp_type, igmp->igmp_code), inetFmt(src, s1), inetFmt(dst, s2) ); switch (igmp->igmp_type) { case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: acceptGroupReport(src, group, igmp->igmp_type); return; case IGMP_V2_LEAVE_GROUP: acceptLeaveMessage(src, group); return; /* case IGMP_MEMBERSHIP_QUERY: //accept_membership_query(src, dst, group, igmp->igmp_code); return; */ default: log_msg(LOG_INFO, 0, "ignoring unknown IGMP message type %x from %s to %s", igmp->igmp_type, inetFmt(src, s1), inetFmt(dst, s2)); return; } }
/** * Adds a specified route to the routingtable. * If the route already exists, the existing route * is updated... */ int insertRoute(uint32_t group, int ifx) { struct Config *conf = getCommonConfig(); struct RouteTable* croute; // Sanitycheck the group adress... if( ! IN_MULTICAST( ntohl(group) )) { my_log(LOG_WARNING, 0, "The group address %s is not a valid Multicast group. Table insert failed.", inetFmt(group, s1)); return 0; } // Santiycheck the VIF index... //if(ifx < 0 || ifx >= MAX_MC_VIFS) { if(ifx >= MAX_MC_VIFS) { my_log(LOG_WARNING, 0, "The VIF Ix %d is out of range (0-%d). Table insert failed.",ifx,MAX_MC_VIFS); return 0; } // Try to find an existing route for this group... croute = findRoute(group); if(croute==NULL) { struct RouteTable* newroute; my_log(LOG_DEBUG, 0, "No existing route for %s. Create new.", inetFmt(group, s1)); // Create and initialize the new route table entry.. newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable)); // Insert the route desc and clear all pointers... newroute->group = group; memset(newroute->originAddrs, 0, MAX_ORIGINS * sizeof(newroute->originAddrs[0])); newroute->nextroute = NULL; newroute->prevroute = NULL; // The group is not joined initially. newroute->upstrState = ROUTESTATE_NOTJOINED; // The route is not active yet, so the age is unimportant. newroute->ageValue = conf->robustnessValue; newroute->ageActivity = 0; BIT_ZERO(newroute->ageVifBits); // Initially we assume no listeners. // Set the listener flag... BIT_ZERO(newroute->vifBits); // Initially no listeners... if(ifx >= 0) { BIT_SET(newroute->vifBits, ifx); } // Check if there is a table already.... if(routing_table == NULL) { // No location set, so insert in on the table top. routing_table = newroute; my_log(LOG_DEBUG, 0, "No routes in table. Insert at beginning."); } else { my_log(LOG_DEBUG, 0, "Found existing routes. Find insert location."); // Check if the route could be inserted at the beginning... if(routing_table->group > group) { my_log(LOG_DEBUG, 0, "Inserting at beginning, before route %s",inetFmt(routing_table->group,s1)); // Insert at beginning... newroute->nextroute = routing_table; newroute->prevroute = NULL; routing_table = newroute; // If the route has a next node, the previous pointer must be updated. if(newroute->nextroute != NULL) { newroute->nextroute->prevroute = newroute; } } else { // Find the location which is closest to the route. for( croute = routing_table; croute->nextroute != NULL; croute = croute->nextroute ) { // Find insert position. if(croute->nextroute->group > group) { break; } } my_log(LOG_DEBUG, 0, "Inserting after route %s",inetFmt(croute->group,s1)); // Insert after current... newroute->nextroute = croute->nextroute; newroute->prevroute = croute; if(croute->nextroute != NULL) { croute->nextroute->prevroute = newroute; } croute->nextroute = newroute; } } // Set the new route as the current... croute = newroute; // Log the cleanup in debugmode... my_log(LOG_INFO, 0, "Inserted route table entry for %s on VIF #%d", inetFmt(croute->group, s1),ifx); } else if(ifx >= 0) { // The route exists already, so just update it. BIT_SET(croute->vifBits, ifx); // Register the VIF activity for the aging routine BIT_SET(croute->ageVifBits, ifx); // Log the cleanup in debugmode... my_log(LOG_INFO, 0, "Updated route entry for %s on VIF #%d", inetFmt(croute->group, s1), ifx); // Update route in kernel... if(!internUpdateKernelRoute(croute, 1)) { my_log(LOG_WARNING, 0, "The insertion into Kernel failed."); return 0; } } // Send join message upstream, if the route has no joined flag... if(croute->upstrState != ROUTESTATE_JOINED) { // Send Join request upstream sendJoinLeaveUpstream(croute, 1); } logRouteTable("Insert Route"); return 1; }