825 lines
20 KiB
C
825 lines
20 KiB
C
|
/*
|
||
|
* Copyright (c) 1998 by the University of Southern California.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Permission to use, copy, modify, and distribute this software and
|
||
|
* its documentation in source and binary forms for lawful
|
||
|
* purposes and without fee is hereby granted, provided
|
||
|
* that the above copyright notice appear in all copies and that both
|
||
|
* the copyright notice and this permission notice appear in supporting
|
||
|
* documentation, and that any documentation, advertising materials,
|
||
|
* and other materials related to such distribution and use acknowledge
|
||
|
* that the software was developed by the University of Southern
|
||
|
* California and/or Information Sciences Institute.
|
||
|
* The name of the University of Southern California may not
|
||
|
* be used to endorse or promote products derived from this software
|
||
|
* without specific prior written permission.
|
||
|
*
|
||
|
* THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
|
||
|
* ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
|
||
|
* PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
|
||
|
* INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
|
||
|
* NON-INFRINGEMENT.
|
||
|
*
|
||
|
* IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
|
||
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
|
||
|
* TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
|
||
|
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
*
|
||
|
* Other copyrights might apply to parts of this software and are so
|
||
|
* noted when applicable.
|
||
|
*
|
||
|
* $FreeBSD$
|
||
|
*/
|
||
|
/*
|
||
|
* Questions concerning this software should be directed to
|
||
|
* Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
* This program has been derived from pim6dd.
|
||
|
* The pim6dd program is covered by the license in the accompanying file
|
||
|
* named "LICENSE.pim6dd".
|
||
|
*/
|
||
|
/*
|
||
|
* This program has been derived from pimd.
|
||
|
* The pimd program is covered by the license in the accompanying file
|
||
|
* named "LICENSE.pimd".
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
* Part of this program has been derived from mrouted.
|
||
|
* The mrouted program is covered by the license in the accompanying file
|
||
|
* named "LICENSE.mrouted".
|
||
|
*
|
||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||
|
* Leland Stanford Junior University.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <errno.h>
|
||
|
#include <syslog.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "vif.h"
|
||
|
#include "mld6.h"
|
||
|
#include "pim6.h"
|
||
|
#include "pimd.h"
|
||
|
#include "route.h"
|
||
|
#include "config.h"
|
||
|
#include "inet6.h"
|
||
|
#include "kern.h"
|
||
|
#include "mld6_proto.h"
|
||
|
#include "pim6_proto.h"
|
||
|
#include "mrt.h"
|
||
|
#include "debug.h"
|
||
|
#include "timer.h"
|
||
|
|
||
|
struct uvif uvifs[MAXMIFS]; /*the list of virtualsinterfaces */
|
||
|
vifi_t numvifs; /*total number of interface */
|
||
|
int vifs_down;
|
||
|
vifi_t reg_vif_num; /*register interface*/
|
||
|
int phys_vif; /* An enabled vif that has a global address */
|
||
|
int udp_socket;
|
||
|
int total_interfaces;
|
||
|
if_set if_nullset;
|
||
|
if_set if_result;
|
||
|
|
||
|
int init_reg_vif();
|
||
|
void start_all_vifs();
|
||
|
void start_vif( vifi_t vifi );
|
||
|
void stop_vif( vifi_t vivi );
|
||
|
int update_reg_vif( vifi_t register_vifi);
|
||
|
|
||
|
extern int cfparse(int, int);
|
||
|
|
||
|
void init_vifs()
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
struct uvif *v;
|
||
|
int enabled_vifs;
|
||
|
|
||
|
numvifs = 0;
|
||
|
reg_vif_num = NO_VIF;
|
||
|
|
||
|
/*
|
||
|
* Configure the vifs based on the interface configuration of
|
||
|
* the kernel and the contents of the configuration file.
|
||
|
* (Open a UDP socket for ioctl use in the config procedures if
|
||
|
* the kernel can't handle IOCTL's on the MLD socket.)
|
||
|
*/
|
||
|
#ifdef IOCTL_OK_ON_RAW_SOCKET
|
||
|
udp_socket = mld6_socket;
|
||
|
#else
|
||
|
if ((udp_socket = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
|
||
|
log(LOG_ERR, errno, "UDP6 socket");
|
||
|
#endif
|
||
|
|
||
|
/* clean all the interfaces ... */
|
||
|
|
||
|
for(vifi = 0,v=uvifs; vifi < MAXVIFS; ++ vifi, ++v)
|
||
|
{
|
||
|
memset(v,0,sizeof(*v)); /* everything is zeroed => NULL , pointer NULL , addrANY ...) */
|
||
|
v->uv_metric = DEFAULT_METRIC;
|
||
|
v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
|
||
|
strncpy(v->uv_name,"",IFNAMSIZ);
|
||
|
v->uv_local_pref = default_source_preference;
|
||
|
v->uv_local_metric = default_source_metric;
|
||
|
}
|
||
|
IF_DEBUG(DEBUG_IF)
|
||
|
log(LOG_DEBUG,0,"Interfaces world initialized...");
|
||
|
IF_DEBUG(DEBUG_IF)
|
||
|
log(LOG_DEBUG,0,"Getting vifs from kernel");
|
||
|
config_vifs_from_kernel();
|
||
|
if (max_global_address() == NULL)
|
||
|
log(LOG_ERR, 0, "There's no global address");
|
||
|
IF_DEBUG(DEBUG_IF)
|
||
|
log(LOG_DEBUG,0,"Getting vifs from %s",configfilename);
|
||
|
|
||
|
/* read config from file */
|
||
|
if (cfparse(1, 0) != 0)
|
||
|
log(LOG_ERR, 0, "fatal error in parsing the config file");
|
||
|
|
||
|
enabled_vifs = 0;
|
||
|
phys_vif = -1;
|
||
|
|
||
|
for( vifi = 0, v = uvifs ; vifi < numvifs ; ++ vifi,++v)
|
||
|
{
|
||
|
if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
|
||
|
continue;
|
||
|
if(v->uv_linklocal == NULL)
|
||
|
log(LOG_ERR,0,"there is no link-local address on vif %s",v->uv_name);
|
||
|
if (phys_vif == -1) {
|
||
|
struct phaddr *p;
|
||
|
|
||
|
/*
|
||
|
* If this vif has a global address, set its id
|
||
|
* to phys_vif.
|
||
|
*/
|
||
|
for(p = v->uv_addrs; p; p = p->pa_next) {
|
||
|
if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
|
||
|
!IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) {
|
||
|
phys_vif = vifi;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
enabled_vifs++;
|
||
|
}
|
||
|
if (enabled_vifs < 2)
|
||
|
log(LOG_ERR,0,"can't forward: %s",
|
||
|
enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif" );
|
||
|
|
||
|
memset(&if_nullset,0,sizeof(if_nullset));
|
||
|
k_init_pim(mld6_socket);
|
||
|
IF_DEBUG(DEBUG_PIM_DETAIL)
|
||
|
log(LOG_DEBUG,0,"Pim kernel initialization done");
|
||
|
|
||
|
|
||
|
/* Add a dummy virtual interface to support Registers in the kernel. */
|
||
|
init_reg_vif();
|
||
|
|
||
|
start_all_vifs();
|
||
|
|
||
|
}
|
||
|
int init_reg_vif()
|
||
|
{
|
||
|
struct uvif *v;
|
||
|
vifi_t i;
|
||
|
|
||
|
v = &uvifs[numvifs];
|
||
|
if (( numvifs+1 ) == MAXVIFS )
|
||
|
{
|
||
|
/* Exit the program! The PIM router must have a Register vif */
|
||
|
log(LOG_ERR, 0,
|
||
|
"cannot install the Register vif: too many interfaces");
|
||
|
/* To make lint happy */
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* So far in PIM we need only one register vif and we save its number in
|
||
|
* the global reg_vif_num.
|
||
|
*/
|
||
|
|
||
|
|
||
|
reg_vif_num = numvifs;
|
||
|
|
||
|
/* Use the address of the first available physical interface to
|
||
|
* create the register vif.
|
||
|
*/
|
||
|
|
||
|
for(i =0 ; i < numvifs ; i++)
|
||
|
{
|
||
|
if(uvifs[i].uv_flags & (VIFF_DOWN | VIFF_DISABLED | MIFF_REGISTER))
|
||
|
continue;
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
if( i >= numvifs)
|
||
|
{
|
||
|
log(LOG_ERR, 0, "No physical interface enabled");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
memcpy(v,&uvifs[i],sizeof(*v));
|
||
|
strncpy(v->uv_name,"register_mif0",IFNAMSIZ);
|
||
|
v->uv_flags = MIFF_REGISTER;
|
||
|
|
||
|
#ifdef PIM_EXPERIMENTAL
|
||
|
v->uv_flags |= MIFF_REGISTER_KERNEL_ENCAP;
|
||
|
#endif
|
||
|
|
||
|
IF_DEBUG(DEBUG_IF)
|
||
|
log(LOG_DEBUG,0,"Interface %s (subnet %s) ,installed on vif #%u - rate = %d",
|
||
|
v->uv_name,net6name(&v->uv_prefix.sin6_addr,&v->uv_subnetmask),
|
||
|
reg_vif_num,v->uv_rate_limit);
|
||
|
|
||
|
numvifs++;
|
||
|
total_interfaces++;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void start_all_vifs()
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
struct uvif *v;
|
||
|
u_int action;
|
||
|
|
||
|
|
||
|
/* Start first the NON-REGISTER vifs */
|
||
|
|
||
|
for(action=0; ;action = MIFF_REGISTER )
|
||
|
{
|
||
|
for(vifi= 0,v = uvifs;vifi < numvifs ; ++vifi, ++v)
|
||
|
{
|
||
|
if (( v->uv_flags & MIFF_REGISTER ) ^ action )
|
||
|
/* If starting non-registers but the vif is a register
|
||
|
* or if starting registers, but the interface is not
|
||
|
* a register, then just continue.
|
||
|
*/
|
||
|
continue;
|
||
|
|
||
|
if ( v->uv_flags & (VIFF_DISABLED | VIFF_DOWN ))
|
||
|
{
|
||
|
IF_DEBUG(DEBUG_IF)
|
||
|
{
|
||
|
if ( v-> uv_flags & VIFF_DISABLED)
|
||
|
log(LOG_DEBUG,0,"%s is DISABLED ; vif #%u out of service",v->uv_name,vifi);
|
||
|
else
|
||
|
log(LOG_DEBUG,0,"%s is DOWN ; vif #%u out of service",v->uv_name,vifi);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
start_vif(vifi);
|
||
|
}
|
||
|
if ( action == MIFF_REGISTER)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Initialize the vif and add to the kernel. The vif can be either
|
||
|
* physical, register or tunnel (tunnels will be used in the future
|
||
|
* when this code becomes PIM multicast boarder router.
|
||
|
*/
|
||
|
|
||
|
|
||
|
void start_vif (vifi_t vifi)
|
||
|
{
|
||
|
struct uvif *v;
|
||
|
|
||
|
v = &uvifs[vifi];
|
||
|
|
||
|
/* Initialy no router on any vif */
|
||
|
|
||
|
if( v-> uv_flags & MIFF_REGISTER)
|
||
|
v->uv_flags = v->uv_flags & ~VIFF_DOWN;
|
||
|
else
|
||
|
{
|
||
|
v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~ VIFF_DOWN;
|
||
|
v->uv_pim_hello_timer = 1 + RANDOM() % pim_hello_period;
|
||
|
v->uv_jp_timer = 1 + RANDOM() % pim_join_prune_period;
|
||
|
}
|
||
|
|
||
|
/* Tell kernel to add, i.e. start this vif */
|
||
|
|
||
|
k_add_vif(mld6_socket,vifi,&uvifs[vifi]);
|
||
|
IF_DEBUG(DEBUG_IF)
|
||
|
log(LOG_DEBUG,0,"%s comes up ,vif #%u now in service",v->uv_name,vifi);
|
||
|
|
||
|
if(!(v->uv_flags & MIFF_REGISTER))
|
||
|
{
|
||
|
|
||
|
/*
|
||
|
* Join the PIM multicast group on the interface.
|
||
|
*/
|
||
|
|
||
|
|
||
|
k_join(mld6_socket,&allpim6routers_group.sin6_addr,v->uv_ifindex);
|
||
|
/*
|
||
|
* Join the ALL-ROUTERS multicast group on the interface.
|
||
|
* This allows mtrace requests to loop back if they are run
|
||
|
* on the multicast router.this allow receiving mld6 messages too.
|
||
|
*/
|
||
|
|
||
|
k_join(mld6_socket,&allrouters_group.sin6_addr,v->uv_ifindex);
|
||
|
|
||
|
/*
|
||
|
* Until neighbors are discovered, assume responsibility for sending
|
||
|
* periodic group membership queries to the subnet. Send the first
|
||
|
* query.
|
||
|
*/
|
||
|
|
||
|
|
||
|
v->uv_flags |= VIFF_QUERIER;
|
||
|
query_groups(v);
|
||
|
|
||
|
/*
|
||
|
* Send a probe via the new vif to look for neighbors.
|
||
|
*/
|
||
|
|
||
|
send_pim6_hello( v , pim_hello_holdtime );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Stop a vif (either physical interface, tunnel or
|
||
|
* register.) If we are running only PIM we don't have tunnels.
|
||
|
*/
|
||
|
|
||
|
|
||
|
void stop_vif( vifi_t vifi )
|
||
|
{
|
||
|
struct uvif *v;
|
||
|
struct listaddr *a;
|
||
|
register pim_nbr_entry_t *n;
|
||
|
register pim_nbr_entry_t *next;
|
||
|
struct vif_acl *acl;
|
||
|
|
||
|
|
||
|
/*
|
||
|
* TODO: make sure that the kernel viftable is
|
||
|
* consistent with the daemon table
|
||
|
*/
|
||
|
|
||
|
v=&uvifs[vifi];
|
||
|
if( !( v->uv_flags&MIFF_REGISTER ) )
|
||
|
{
|
||
|
k_leave( mld6_socket , &allpim6routers_group.sin6_addr , v->uv_ifindex );
|
||
|
k_leave( mld6_socket , &allrouters_group.sin6_addr , v->uv_ifindex );
|
||
|
/*
|
||
|
* Discard all group addresses. (No need to tell kernel;
|
||
|
* the k_del_vif() call will clean up kernel state.)
|
||
|
*/
|
||
|
|
||
|
while( v->uv_groups!=NULL )
|
||
|
{
|
||
|
a=v->uv_groups;
|
||
|
v->uv_groups=a->al_next;
|
||
|
free((char *)a);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* TODO: inform (eventually) the neighbors I am going down by sending
|
||
|
* PIM_HELLO with holdtime=0 so someone else should become a DR.
|
||
|
*/
|
||
|
/* TODO: dummy! Implement it!! Any problems if don't use it? */
|
||
|
delete_vif_from_mrt(vifi);
|
||
|
|
||
|
/*
|
||
|
* Delete the interface from the kernel's vif structure.
|
||
|
*/
|
||
|
|
||
|
k_del_vif( mld6_socket , vifi );
|
||
|
v->uv_flags=(v->uv_flags & ~VIFF_DR & ~VIFF_QUERIER & ~VIFF_NONBRS) | VIFF_DOWN;
|
||
|
if( !(v->uv_flags & MIFF_REGISTER ))
|
||
|
{
|
||
|
RESET_TIMER(v->uv_pim_hello_timer);
|
||
|
RESET_TIMER(v->uv_jp_timer);
|
||
|
RESET_TIMER(v->uv_gq_timer);
|
||
|
|
||
|
for( n=v->uv_pim_neighbors ; n!=NULL ; n = next )
|
||
|
{
|
||
|
next=n->next; /* Free the space for each neighbour */
|
||
|
free((char *)n);
|
||
|
}
|
||
|
v->uv_pim_neighbors=NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* TODO: currently not used */
|
||
|
/* The Access Control List (list with the scoped addresses) */
|
||
|
|
||
|
while( v->uv_acl!=NULL )
|
||
|
{
|
||
|
acl=v->uv_acl;
|
||
|
v->uv_acl=acl->acl_next;
|
||
|
free((char *)acl);
|
||
|
}
|
||
|
|
||
|
vifs_down=TRUE;
|
||
|
|
||
|
IF_DEBUG(DEBUG_IF)
|
||
|
log( LOG_DEBUG ,0,"%s goes down , vif #%u out of service" , v->uv_name , vifi);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Update the register vif in the multicast routing daemon and the
|
||
|
* kernel because the interface used initially to get its local address
|
||
|
* is DOWN. register_vifi is the index to the Register vif which needs
|
||
|
* to be updated. As a result the Register vif has a new uv_lcl_addr and
|
||
|
* is UP (virtually :))
|
||
|
*/
|
||
|
int
|
||
|
update_reg_vif( vifi_t register_vifi )
|
||
|
{
|
||
|
register struct uvif *v;
|
||
|
register vifi_t vifi;
|
||
|
|
||
|
/* Find the first useable vif with solid physical background */
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||
|
if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL
|
||
|
| MIFF_REGISTER))
|
||
|
continue;
|
||
|
/* Found. Stop the bogus Register vif first */
|
||
|
stop_vif(register_vifi);
|
||
|
uvifs[register_vifi].uv_linklocal->pa_addr =
|
||
|
uvifs[vifi].uv_linklocal->pa_addr;
|
||
|
start_vif(register_vifi);
|
||
|
IF_DEBUG(DEBUG_PIM_REGISTER | DEBUG_IF)
|
||
|
log(LOG_NOTICE, 0, "%s has come up; vif #%u now in service",
|
||
|
uvifs[register_vifi].uv_name, register_vifi);
|
||
|
return 0;
|
||
|
}
|
||
|
vifs_down = TRUE;
|
||
|
log(LOG_WARNING, 0, "Cannot start Register vif: %s",
|
||
|
uvifs[vifi].uv_name);
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return the max global Ipv6 address of an UP and ENABLED interface
|
||
|
* other than the MIFF_REGISTER interface.
|
||
|
*/
|
||
|
struct sockaddr_in6 *
|
||
|
max_global_address()
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
struct uvif *v;
|
||
|
struct phaddr *p;
|
||
|
struct phaddr *pmax = NULL;
|
||
|
|
||
|
for(vifi=0,v=uvifs;vifi< numvifs;++vifi,++v)
|
||
|
{
|
||
|
if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
|
||
|
continue;
|
||
|
/*
|
||
|
* take first the max global address of the interface
|
||
|
* (without link local) => aliasing
|
||
|
*/
|
||
|
for(p=v->uv_addrs;p!=NULL;p=p->pa_next)
|
||
|
{
|
||
|
/*
|
||
|
* If this is the first global address, take it anyway.
|
||
|
*/
|
||
|
if (pmax == NULL) {
|
||
|
if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
|
||
|
!IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
|
||
|
pmax = p;
|
||
|
}
|
||
|
else {
|
||
|
if (inet6_lessthan(&pmax->pa_addr,
|
||
|
&p->pa_addr) &&
|
||
|
!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
|
||
|
!IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
|
||
|
pmax=p;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(pmax ? &pmax->pa_addr : NULL);
|
||
|
}
|
||
|
|
||
|
struct sockaddr_in6 *
|
||
|
uv_global(vifi)
|
||
|
vifi_t vifi;
|
||
|
{
|
||
|
struct uvif *v = &uvifs[vifi];
|
||
|
struct phaddr *p;
|
||
|
|
||
|
for (p = v->uv_addrs; p; p = p->pa_next) {
|
||
|
if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
|
||
|
!IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
|
||
|
return(&p->pa_addr);
|
||
|
}
|
||
|
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check if the interface exists in the mif table. If true
|
||
|
* return the highest address of the interface else return NULL.
|
||
|
*/
|
||
|
struct sockaddr_in6 *
|
||
|
local_iface(char *ifname)
|
||
|
{
|
||
|
register struct uvif *v;
|
||
|
vifi_t vifi;
|
||
|
struct phaddr *p;
|
||
|
struct phaddr *pmax = NULL;
|
||
|
|
||
|
for(vifi=0,v=uvifs;vifi<numvifs;++vifi,++v)
|
||
|
{
|
||
|
if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
|
||
|
continue;
|
||
|
if(EQUAL(v->uv_name, ifname))
|
||
|
{
|
||
|
for(p=v->uv_addrs; p!=NULL; p=p->pa_next)
|
||
|
{
|
||
|
if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr)&&
|
||
|
!IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) {
|
||
|
/*
|
||
|
* If this is the first global address
|
||
|
* or larger than the current MAX global
|
||
|
* address, remember it.
|
||
|
*/
|
||
|
if (pmax == NULL ||
|
||
|
inet6_lessthan(&pmax->pa_addr,
|
||
|
&p->pa_addr))
|
||
|
pmax = p;
|
||
|
}
|
||
|
}
|
||
|
if (pmax)
|
||
|
return(&pmax->pa_addr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* See if any interfaces have changed from up state to down, or vice versa,
|
||
|
* including any non-multicast-capable interfaces that are in use as local
|
||
|
* tunnel end-points. Ignore interfaces that have been administratively
|
||
|
* disabled.
|
||
|
*/
|
||
|
void
|
||
|
check_vif_state()
|
||
|
{
|
||
|
register vifi_t vifi;
|
||
|
register struct uvif *v;
|
||
|
struct ifreq ifr;
|
||
|
static int checking_vifs=0;
|
||
|
|
||
|
/*
|
||
|
* XXX: TODO: True only for DVMRP?? Check.
|
||
|
* If we get an error while checking, (e.g. two interfaces go down
|
||
|
* at once, and we decide to send a prune out one of the failed ones)
|
||
|
* then don't go into an infinite loop!
|
||
|
*/
|
||
|
if( checking_vifs )
|
||
|
return;
|
||
|
|
||
|
vifs_down=FALSE;
|
||
|
checking_vifs=TRUE;
|
||
|
|
||
|
/* TODO: Check all potential interfaces!!! */
|
||
|
/* Check the physical and tunnels only */
|
||
|
for( vifi=0 , v=uvifs ; vifi<numvifs ; ++vifi , ++v )
|
||
|
{
|
||
|
if( v->uv_flags & ( VIFF_DISABLED|MIFF_REGISTER ) )
|
||
|
continue;
|
||
|
|
||
|
strncpy( ifr.ifr_name , v->uv_name , IFNAMSIZ );
|
||
|
|
||
|
/* get the interface flags */
|
||
|
if( ioctl( udp_socket , SIOCGIFFLAGS , (char *)&ifr )<0 )
|
||
|
log(LOG_ERR, errno,
|
||
|
"check_vif_state: ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
|
||
|
|
||
|
if( v->uv_flags & VIFF_DOWN )
|
||
|
{
|
||
|
if ( ifr.ifr_flags & IFF_UP )
|
||
|
{
|
||
|
start_vif( vifi );
|
||
|
}
|
||
|
else
|
||
|
vifs_down=TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( !( ifr.ifr_flags & IFF_UP ))
|
||
|
{
|
||
|
log( LOG_NOTICE ,0,
|
||
|
"%s has gone down ; vif #%u taken out of service",
|
||
|
v->uv_name , vifi );
|
||
|
stop_vif ( vifi );
|
||
|
vifs_down = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Check the register(s) vif(s) */
|
||
|
for( vifi=0 , v=uvifs ; vifi<numvifs ; ++vifi , ++v )
|
||
|
{
|
||
|
register vifi_t vifi2;
|
||
|
register struct uvif *v2;
|
||
|
int found;
|
||
|
|
||
|
if( !(v->uv_flags & MIFF_REGISTER ) )
|
||
|
continue;
|
||
|
else
|
||
|
{
|
||
|
found=0;
|
||
|
|
||
|
/* Find a physical vif with the same IP address as the
|
||
|
* Register vif.
|
||
|
*/
|
||
|
for( vifi2=0 , v2=uvifs ; vifi2<numvifs ; ++vifi2 , ++v2 )
|
||
|
{
|
||
|
if( v2->uv_flags & ( VIFF_DISABLED|VIFF_DOWN|VIFF_TUNNEL|MIFF_REGISTER ))
|
||
|
continue;
|
||
|
if( IN6_ARE_ADDR_EQUAL( &v->uv_linklocal->pa_addr.sin6_addr,
|
||
|
&v2->uv_linklocal->pa_addr.sin6_addr ))
|
||
|
{
|
||
|
found=1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if(!found)
|
||
|
/* The physical interface with the IP address as the Register
|
||
|
* vif is probably DOWN. Get a replacement.
|
||
|
*/
|
||
|
update_reg_vif( vifi );
|
||
|
}
|
||
|
}
|
||
|
checking_vifs=0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If the source is directly connected to us, find the vif number for
|
||
|
* the corresponding physical interface (tunnels excluded).
|
||
|
* Local addresses are excluded.
|
||
|
* Return the vif number or NO_VIF if not found.
|
||
|
*/
|
||
|
|
||
|
vifi_t
|
||
|
find_vif_direct(src)
|
||
|
struct sockaddr_in6 *src;
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
register struct uvif *v;
|
||
|
register struct phaddr *p;
|
||
|
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
|
||
|
{
|
||
|
if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL|MIFF_REGISTER))
|
||
|
continue;
|
||
|
for (p = v->uv_addrs; p; p = p->pa_next)
|
||
|
{
|
||
|
if (inet6_equal(src, &p->pa_addr))
|
||
|
return(NO_VIF);
|
||
|
if (inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask))
|
||
|
return(vifi);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (NO_VIF);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Checks if src is local address. If "yes" return the vif index,
|
||
|
* otherwise return value is NO_VIF.
|
||
|
*/
|
||
|
|
||
|
vifi_t
|
||
|
local_address(src)
|
||
|
struct sockaddr_in6 *src;
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
register struct uvif *v;
|
||
|
register struct phaddr *p;
|
||
|
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||
|
if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
|
||
|
continue;
|
||
|
for (p = v->uv_addrs; p; p = p->pa_next) {
|
||
|
if (inet6_equal(src, &p->pa_addr))
|
||
|
return(vifi);
|
||
|
}
|
||
|
}
|
||
|
/* Returning NO_VIF means not a local address */
|
||
|
return (NO_VIF);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* If the source is directly connected, or is local address,
|
||
|
* find the vif number for the corresponding physical interface
|
||
|
* (tunnels excluded).
|
||
|
* Return the vif number or NO_VIF if not found.
|
||
|
*/
|
||
|
|
||
|
vifi_t
|
||
|
find_vif_direct_local(src)
|
||
|
struct sockaddr_in6 *src;
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
register struct uvif *v;
|
||
|
register struct phaddr *p;
|
||
|
|
||
|
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||
|
if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL |MIFF_REGISTER))
|
||
|
continue;
|
||
|
for (p = v->uv_addrs; p; p = p->pa_next) {
|
||
|
if (inet6_equal(src, &p->pa_addr) ||
|
||
|
inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask))
|
||
|
return(vifi);
|
||
|
}
|
||
|
}
|
||
|
return (NO_VIF);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
vif_forwarder(if_set *p1 , if_set *p2)
|
||
|
{
|
||
|
int idx;
|
||
|
|
||
|
for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
|
||
|
{
|
||
|
if (p1->ifs_bits[idx] & p2->ifs_bits[idx])
|
||
|
return(TRUE);
|
||
|
|
||
|
}
|
||
|
|
||
|
/* (p1 & p2) is empty. We're not the forwarder */
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
if_set *
|
||
|
vif_and(if_set *p1 , if_set *p2, if_set *result)
|
||
|
{
|
||
|
int idx;
|
||
|
|
||
|
IF_ZERO(result);
|
||
|
|
||
|
for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
|
||
|
{
|
||
|
result->ifs_bits[idx] = p1->ifs_bits[idx] & p2->ifs_bits[idx];
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
if_set *
|
||
|
vif_xor(if_set *p1 , if_set *p2, if_set *result)
|
||
|
{
|
||
|
int idx;
|
||
|
|
||
|
IF_ZERO(result);
|
||
|
|
||
|
for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
|
||
|
{
|
||
|
result->ifs_bits[idx] =
|
||
|
p1->ifs_bits[idx] ^ p2->ifs_bits[idx];
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
/*
|
||
|
* stop all vifs
|
||
|
*/
|
||
|
void
|
||
|
stop_all_vifs()
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
struct uvif *v;
|
||
|
|
||
|
for (vifi = 0, v=uvifs; vifi < numvifs; ++vifi, ++v) {
|
||
|
if (!(v->uv_flags & VIFF_DOWN)) {
|
||
|
stop_vif(vifi);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct uvif *
|
||
|
find_vif(ifname)
|
||
|
char *ifname;
|
||
|
{
|
||
|
struct uvif *v;
|
||
|
vifi_t vifi;
|
||
|
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v) {
|
||
|
if (strcasecmp(v->uv_name, ifname) == 0)
|
||
|
return(v);
|
||
|
}
|
||
|
|
||
|
return(NULL);
|
||
|
}
|