blob: 3a7476dcb430c2a083aac5e1760b605b4d5625c6 [file] [log] [blame]
/*
** 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;
}