| /* |
| ** 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, either version 2 or |
| ** any later version. |
| ** |
| ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of |
| ** Leland Stanford Junior University. |
| ** - Licensed under the 3-clause BSD license, see Stanford.txt file. |
| ** |
| */ |
| |
| #include "igmpproxy.h" |
| |
| /* We need a temporary copy to not break strict aliasing rules */ |
| static inline uint32_t s_addr_from_sockaddr(const struct sockaddr *addr) { |
| struct sockaddr_in addr_in; |
| memcpy(&addr_in, addr, sizeof(addr_in)); |
| return addr_in.sin_addr.s_addr; |
| } |
| |
| struct IfDesc IfDescVc[ MAX_IF ], *IfDescEp = IfDescVc; |
| |
| /* aimwang: add for detect interface and rebuild IfVc record */ |
| /*************************************************** |
| * TODO: Only need run me when detect downstream changed. |
| * For example: /etc/ppp/ip-up & ip-down can touch a file /tmp/ppp_changed |
| * So I can check if the file exist then run me and delete the file. |
| ***************************************************/ |
| void rebuildIfVc () { |
| struct ifreq IfVc[ sizeof( IfDescVc ) / sizeof( IfDescVc[ 0 ] ) ]; |
| struct ifreq *IfEp; |
| struct ifconf IoCtlReq; |
| struct IfDesc *Dp; |
| struct ifreq *IfPt, *IfNext; |
| uint32_t addr, subnet, mask; |
| int Sock; |
| |
| // Get the config. |
| struct Config *config = getCommonConfig(); |
| |
| if ( (Sock = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) |
| my_log( LOG_ERR, errno, "RAW socket open" ); |
| |
| // aimwang: set all downstream IF as lost, for check IF exist or gone. |
| for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { |
| if (Dp->state == IF_STATE_DOWNSTREAM) { |
| Dp->state = IF_STATE_LOST; |
| } |
| } |
| |
| IoCtlReq.ifc_buf = (void *)IfVc; |
| IoCtlReq.ifc_len = sizeof( IfVc ); |
| |
| if ( ioctl( Sock, SIOCGIFCONF, &IoCtlReq ) < 0 ) |
| my_log( LOG_ERR, errno, "ioctl SIOCGIFCONF" ); |
| |
| IfEp = (void *)((char *)IfVc + IoCtlReq.ifc_len); |
| |
| for ( IfPt = IfVc; IfPt < IfEp; IfPt = IfNext ) { |
| struct ifreq IfReq; |
| char FmtBu[ 32 ]; |
| |
| IfNext = (struct ifreq *)((char *)&IfPt->ifr_addr + |
| #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN |
| IfPt->ifr_addr.sa_len |
| #else |
| sizeof(struct sockaddr_in) |
| #endif |
| ); |
| if (IfNext < IfPt + 1) |
| IfNext = IfPt + 1; |
| |
| for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { |
| if (0 == strcmp(Dp->Name, IfPt->ifr_name)) { |
| break; |
| } |
| } |
| |
| if (Dp == IfDescEp) { |
| strncpy( Dp->Name, IfPt->ifr_name, sizeof( IfDescEp->Name ) ); |
| } |
| |
| if ( IfPt->ifr_addr.sa_family != AF_INET ) { |
| if (Dp == IfDescEp) { |
| IfDescEp++; |
| } |
| Dp->InAdr.s_addr = 0; /* mark as non-IP interface */ |
| continue; |
| } |
| |
| // Get the interface adress... |
| Dp->InAdr.s_addr = s_addr_from_sockaddr(&IfPt->ifr_addr); |
| addr = Dp->InAdr.s_addr; |
| |
| memcpy( IfReq.ifr_name, Dp->Name, sizeof( IfReq.ifr_name ) ); |
| |
| // Get the subnet mask... |
| if (ioctl(Sock, SIOCGIFNETMASK, &IfReq ) < 0) |
| my_log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", IfReq.ifr_name); |
| mask = s_addr_from_sockaddr(&IfReq.ifr_netmask); |
| subnet = addr & mask; |
| |
| if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 ) |
| my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" ); |
| Dp->Flags = IfReq.ifr_flags; |
| |
| if (0x10d1 == Dp->Flags) |
| { |
| if ( ioctl( Sock, SIOCGIFDSTADDR, &IfReq ) < 0 ) |
| my_log(LOG_ERR, errno, "ioctl SIOCGIFDSTADDR for %s", IfReq.ifr_name); |
| addr = s_addr_from_sockaddr(&IfReq.ifr_dstaddr); |
| subnet = addr & mask; |
| } |
| |
| if (Dp == IfDescEp) { |
| // Insert the verified subnet as an allowed net... |
| Dp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); |
| if(IfDescEp->allowednets == NULL) { |
| my_log(LOG_ERR, 0, "Out of memory !"); |
| } |
| Dp->allowednets->next = NULL; |
| Dp->state = IF_STATE_DOWNSTREAM; |
| Dp->robustness = DEFAULT_ROBUSTNESS; |
| Dp->threshold = DEFAULT_THRESHOLD; /* ttl limit */ |
| Dp->ratelimit = DEFAULT_RATELIMIT; |
| } |
| |
| // Set the network address for the IF.. |
| Dp->allowednets->subnet_mask = mask; |
| Dp->allowednets->subnet_addr = subnet; |
| |
| // Set the state for the IF... |
| if (Dp->state == IF_STATE_LOST) { |
| Dp->state = IF_STATE_DOWNSTREAM; |
| } |
| |
| // when IF become enabeld from downstream, addVIF to enable its VIF |
| if (Dp->state == IF_STATE_HIDDEN) { |
| my_log(LOG_NOTICE, 0, "%s [Hidden -> Downstream]", Dp->Name); |
| Dp->state = IF_STATE_DOWNSTREAM; |
| addVIF(Dp); |
| joinMcGroup(getMcGroupSock(), Dp, allrouters_group); |
| } |
| |
| // addVIF when found new IF |
| if (Dp == IfDescEp) { |
| my_log(LOG_NOTICE, 0, "%s [New]", Dp->Name); |
| Dp->state = config->defaultInterfaceState; |
| addVIF(Dp); |
| joinMcGroup(getMcGroupSock(), Dp, allrouters_group); |
| IfDescEp++; |
| } |
| |
| // Debug log the result... |
| my_log( LOG_DEBUG, 0, "rebuildIfVc: Interface %s Addr: %s, Flags: 0x%04x, Network: %s", |
| Dp->Name, |
| fmtInAdr( FmtBu, Dp->InAdr ), |
| Dp->Flags, |
| inetFmts(subnet, mask, s1)); |
| } |
| |
| // aimwang: search not longer exist IF, set as hidden and call delVIF |
| for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { |
| if (IF_STATE_LOST == Dp->state) { |
| my_log(LOG_NOTICE, 0, "%s [Downstream -> Hidden]", Dp->Name); |
| Dp->state = IF_STATE_HIDDEN; |
| leaveMcGroup( getMcGroupSock(), Dp, allrouters_group ); |
| delVIF(Dp); |
| } |
| } |
| |
| close( Sock ); |
| } |
| |
| /* |
| ** Builds up a vector with the interface of the machine. Calls to the other functions of |
| ** the module will fail if they are called before the vector is build. |
| ** |
| */ |
| void buildIfVc(void) { |
| struct ifreq IfVc[ sizeof( IfDescVc ) / sizeof( IfDescVc[ 0 ] ) ]; |
| struct ifreq *IfEp; |
| struct Config *config = getCommonConfig(); |
| |
| int Sock; |
| |
| if ( (Sock = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) |
| my_log( LOG_ERR, errno, "RAW socket open" ); |
| |
| /* get If vector |
| */ |
| { |
| struct ifconf IoCtlReq; |
| |
| IoCtlReq.ifc_buf = (void *)IfVc; |
| IoCtlReq.ifc_len = sizeof( IfVc ); |
| |
| if ( ioctl( Sock, SIOCGIFCONF, &IoCtlReq ) < 0 ) |
| my_log( LOG_ERR, errno, "ioctl SIOCGIFCONF" ); |
| |
| IfEp = (void *)((char *)IfVc + IoCtlReq.ifc_len); |
| } |
| |
| /* loop over interfaces and copy interface info to IfDescVc |
| */ |
| { |
| struct ifreq *IfPt, *IfNext; |
| |
| // Temp keepers of interface params... |
| uint32_t addr, subnet, mask; |
| |
| for ( IfPt = IfVc; IfPt < IfEp; IfPt = IfNext ) { |
| struct ifreq IfReq; |
| char FmtBu[ 32 ]; |
| |
| IfNext = (struct ifreq *)((char *)&IfPt->ifr_addr + |
| #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN |
| IfPt->ifr_addr.sa_len |
| #else |
| sizeof(struct sockaddr_in) |
| #endif |
| ); |
| if (IfNext < IfPt + 1) |
| IfNext = IfPt + 1; |
| |
| strncpy( IfDescEp->Name, IfPt->ifr_name, sizeof( IfDescEp->Name ) ); |
| |
| // Currently don't set any allowed nets... |
| //IfDescEp->allowednets = NULL; |
| |
| // Set the index to -1 by default. |
| IfDescEp->index = (unsigned int)-1; |
| |
| /* don't retrieve more info for non-IP interfaces |
| */ |
| if ( IfPt->ifr_addr.sa_family != AF_INET ) { |
| IfDescEp->InAdr.s_addr = 0; /* mark as non-IP interface */ |
| IfDescEp++; |
| continue; |
| } |
| |
| // Get the interface adress... |
| IfDescEp->InAdr.s_addr = s_addr_from_sockaddr(&IfPt->ifr_addr); |
| addr = IfDescEp->InAdr.s_addr; |
| |
| memcpy( IfReq.ifr_name, IfDescEp->Name, sizeof( IfReq.ifr_name ) ); |
| |
| // Get the subnet mask... |
| if (ioctl(Sock, SIOCGIFNETMASK, &IfReq ) < 0) |
| my_log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", IfReq.ifr_name); |
| mask = s_addr_from_sockaddr(&IfReq.ifr_netmask); |
| subnet = addr & mask; |
| |
| /* get if flags |
| ** |
| ** typical flags: |
| ** lo 0x0049 -> Running, Loopback, Up |
| ** ethx 0x1043 -> Multicast, Running, Broadcast, Up |
| ** ipppx 0x0091 -> NoArp, PointToPoint, Up |
| ** grex 0x00C1 -> NoArp, Running, Up |
| ** ipipx 0x00C1 -> NoArp, Running, Up |
| */ |
| if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 ) |
| my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" ); |
| |
| IfDescEp->Flags = IfReq.ifr_flags; |
| |
| // aimwang: when pppx get dstaddr for use |
| if (0x10d1 == IfDescEp->Flags) |
| { |
| if ( ioctl( Sock, SIOCGIFDSTADDR, &IfReq ) < 0 ) |
| my_log(LOG_ERR, errno, "ioctl SIOCGIFDSTADDR for %s", IfReq.ifr_name); |
| addr = s_addr_from_sockaddr(&IfReq.ifr_dstaddr); |
| subnet = addr & mask; |
| } |
| |
| // Insert the verified subnet as an allowed net... |
| IfDescEp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); |
| if(IfDescEp->allowednets == NULL) my_log(LOG_ERR, 0, "Out of memory !"); |
| |
| // Create the network address for the IF.. |
| IfDescEp->allowednets->next = NULL; |
| IfDescEp->allowednets->subnet_mask = mask; |
| IfDescEp->allowednets->subnet_addr = subnet; |
| |
| // Set the default params for the IF... |
| IfDescEp->state = config->defaultInterfaceState; |
| IfDescEp->robustness = DEFAULT_ROBUSTNESS; |
| IfDescEp->threshold = DEFAULT_THRESHOLD; /* ttl limit */ |
| IfDescEp->ratelimit = DEFAULT_RATELIMIT; |
| |
| // Debug log the result... |
| my_log( LOG_DEBUG, 0, "buildIfVc: Interface %s Addr: %s, Flags: 0x%04x, Network: %s", |
| IfDescEp->Name, |
| fmtInAdr( FmtBu, IfDescEp->InAdr ), |
| IfDescEp->Flags, |
| inetFmts(subnet,mask, s1)); |
| |
| IfDescEp++; |
| } |
| } |
| |
| close( Sock ); |
| } |
| |
| /* |
| ** Returns a pointer to the IfDesc of the interface 'IfName' |
| ** |
| ** returns: - pointer to the IfDesc of the requested interface |
| ** - NULL if no interface 'IfName' exists |
| ** |
| */ |
| struct IfDesc *getIfByName( const char *IfName ) { |
| struct IfDesc *Dp; |
| |
| for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) |
| if ( ! strcmp( IfName, Dp->Name ) ) |
| return Dp; |
| |
| return NULL; |
| } |
| |
| /* |
| ** Returns a pointer to the IfDesc of the interface 'Ix' |
| ** |
| ** returns: - pointer to the IfDesc of the requested interface |
| ** - NULL if no interface 'Ix' exists |
| ** |
| */ |
| struct IfDesc *getIfByIx( unsigned Ix ) { |
| struct IfDesc *Dp = &IfDescVc[ Ix ]; |
| return Dp < IfDescEp ? Dp : NULL; |
| } |
| |
| /** |
| * Returns a pointer to the IfDesc whose subnet matches |
| * the supplied IP adress. The IP must match a interfaces |
| * subnet, or any configured allowed subnet on a interface. |
| */ |
| struct IfDesc *getIfByAddress( uint32_t ipaddr ) { |
| |
| struct IfDesc *Dp; |
| struct SubnetList *currsubnet; |
| struct IfDesc *res = NULL; |
| uint32_t last_subnet_mask = 0; |
| |
| for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) { |
| // Loop through all registered allowed nets of the VIF... |
| for(currsubnet = Dp->allowednets; currsubnet != NULL; currsubnet = currsubnet->next) { |
| // Check if the ip falls in under the subnet.... |
| if(currsubnet->subnet_mask > last_subnet_mask && (ipaddr & currsubnet->subnet_mask) == currsubnet->subnet_addr) { |
| res = Dp; |
| last_subnet_mask = currsubnet->subnet_mask; |
| } |
| } |
| } |
| return res; |
| } |
| |
| |
| /** |
| * Returns a pointer to the IfDesc whose subnet matches |
| * the supplied IP adress. The IP must match a interfaces |
| * subnet, or any configured allowed subnet on a interface. |
| */ |
| struct IfDesc *getIfByVifIndex( unsigned vifindex ) { |
| struct IfDesc *Dp; |
| if(vifindex>0) { |
| for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) { |
| if(Dp->index == vifindex) { |
| return Dp; |
| } |
| } |
| } |
| return NULL; |
| } |
| |
| |
| /** |
| * Function that checks if a given ipaddress is a valid |
| * address for the supplied VIF. |
| */ |
| int isAdressValidForIf( struct IfDesc* intrface, uint32_t ipaddr ) { |
| struct SubnetList *currsubnet; |
| |
| if(intrface == NULL) { |
| return 0; |
| } |
| |
| // Loop through all registered allowed nets of the VIF... |
| for(currsubnet = intrface->allowednets; currsubnet != NULL; currsubnet = currsubnet->next) { |
| // Check if the ip falls in under the subnet.... |
| if((ipaddr & currsubnet->subnet_mask) == (currsubnet->subnet_addr& currsubnet->subnet_mask)) { |
| return 1; |
| } |
| } |
| return 0; |
| } |