mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-23 11:18:54 +00:00
529 lines
14 KiB
C
529 lines
14 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.
|
||
|
*/
|
||
|
/*
|
||
|
* Questions concerning this software should be directed to
|
||
|
* Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
|
||
|
*
|
||
|
* $Id: vif.c,v 1.3 1999/09/12 17:00:11 jinmei Exp $
|
||
|
*/
|
||
|
/*
|
||
|
* 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.
|
||
|
*
|
||
|
* $FreeBSD$
|
||
|
*/
|
||
|
|
||
|
#include "defs.h"
|
||
|
|
||
|
/*
|
||
|
* Exported variables.
|
||
|
*/
|
||
|
struct uvif uvifs[MAXMIFS]; /* array of all virtual interfaces */
|
||
|
vifi_t numvifs; /* Number of vifs in use */
|
||
|
int vifs_down; /* 1=>some interfaces are down */
|
||
|
int phys_vif; /* An enabled vif */
|
||
|
int udp_socket; /* Since the honkin' kernel doesn't support */
|
||
|
/* ioctls on raw IP sockets, we need a UDP */
|
||
|
/* socket as well as our IGMP (raw) socket. */
|
||
|
/* How dumb. */
|
||
|
int total_interfaces; /* Number of all interfaces: including the
|
||
|
* non-configured, but excluding the
|
||
|
* loopback interface and the non-multicast
|
||
|
* capable interfaces.
|
||
|
*/
|
||
|
if_set if_nullset; /* an interface face that has all-0 bit
|
||
|
* (for comparison)
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Forward declarations.
|
||
|
*/
|
||
|
static void start_vif __P((vifi_t vifi));
|
||
|
static void stop_vif __P((vifi_t vifi));
|
||
|
static void start_all_vifs __P((void));
|
||
|
|
||
|
|
||
|
void
|
||
|
init_vifs()
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
struct uvif *v;
|
||
|
int enabled_vifs;
|
||
|
|
||
|
numvifs = 0;
|
||
|
vifs_down = FALSE;
|
||
|
|
||
|
/*
|
||
|
* 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 up all vifs
|
||
|
*/
|
||
|
for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) {
|
||
|
memset(v, 0, sizeof(*v));
|
||
|
v->uv_flags = 0;
|
||
|
v->uv_metric = DEFAULT_METRIC;
|
||
|
v->uv_admetric = 0;
|
||
|
v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
|
||
|
strncpy(v->uv_name, "", IFNAMSIZ);
|
||
|
v->uv_groups = (struct listaddr *)NULL;
|
||
|
v->uv_dvmrp_neighbors = (struct listaddr *)NULL;
|
||
|
NBRM_CLRALL(v->uv_nbrmap);
|
||
|
v->uv_querier = (struct listaddr *)NULL;
|
||
|
v->uv_prune_lifetime = 0;
|
||
|
v->uv_acl = (struct vif_acl *)NULL;
|
||
|
v->uv_leaf_timer = 0;
|
||
|
v->uv_addrs = (struct phaddr *)NULL;
|
||
|
v->uv_filter = (struct vif_filter *)NULL;
|
||
|
v->uv_pim_hello_timer = 0;
|
||
|
v->uv_gq_timer = 0;
|
||
|
v->uv_pim_neighbors = (struct pim_nbr_entry *)NULL;
|
||
|
v->uv_local_pref = default_source_preference;
|
||
|
v->uv_local_metric = default_source_metric;
|
||
|
}
|
||
|
|
||
|
log(LOG_INFO, 0, "Getting ifs from kernel");
|
||
|
config_vifs_from_kernel();
|
||
|
log(LOG_INFO, 0, "Getting ifs from %s", configfilename);
|
||
|
config_vifs_from_file();
|
||
|
|
||
|
/*
|
||
|
* Quit if there are fewer than two enabled vifs or there is a vif
|
||
|
* which has no link local address.
|
||
|
*/
|
||
|
enabled_vifs = 0;
|
||
|
phys_vif = -1;
|
||
|
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||
|
if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN))
|
||
|
continue;
|
||
|
if (v->uv_linklocal == NULL)
|
||
|
log(LOG_ERR, 0,
|
||
|
"there is no link-local address on vif#%d", vifi);
|
||
|
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 ifs" : "only one enabled if");
|
||
|
|
||
|
memset(&if_nullset, 0, sizeof(if_nullset));
|
||
|
k_init_pim(mld6_socket); /* Call to kernel to initiliaze structures */
|
||
|
|
||
|
start_all_vifs();
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
start_all_vifs()
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
struct uvif *v;
|
||
|
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||
|
/* Start vif if not DISABLED or DOWN */
|
||
|
if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) {
|
||
|
if (v->uv_flags & VIFF_DISABLED)
|
||
|
log(LOG_INFO, 0,
|
||
|
"%s is DISABLED; if #%u out of service",
|
||
|
v->uv_name, vifi);
|
||
|
else
|
||
|
log(LOG_INFO, 0,
|
||
|
"%s is DOWN; if #%u out of service",
|
||
|
v->uv_name, vifi);
|
||
|
}
|
||
|
else
|
||
|
start_vif(vifi);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Initialize the vif and add to the kernel. The vif can be either
|
||
|
* physical, tunnel (tunnels will be used in the future
|
||
|
* when this code becomes PIM multicast boarder router.)
|
||
|
*/
|
||
|
static void
|
||
|
start_vif(vifi)
|
||
|
vifi_t vifi;
|
||
|
{
|
||
|
struct uvif *v;
|
||
|
u_long random_delay;
|
||
|
|
||
|
v = &uvifs[vifi];
|
||
|
/* Initialy no router on any vif */
|
||
|
v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~VIFF_DOWN;
|
||
|
v->uv_pim_hello_timer = 1 + RANDOM() % PIM_TIMER_HELLO_PERIOD;
|
||
|
/* TODO: CHECK THE TIMERS!!!!! Set or reset? */
|
||
|
v->uv_gq_timer = 0;
|
||
|
v->uv_pim_neighbors = (pim_nbr_entry_t *)NULL;
|
||
|
|
||
|
/* Tell kernel to add, i.e. start this vif */
|
||
|
k_add_vif(mld6_socket, vifi, &uvifs[vifi]);
|
||
|
log(LOG_INFO, 0, "%s comes up; if #%u now in service", v->uv_name, vifi);
|
||
|
|
||
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
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);
|
||
|
|
||
|
/*
|
||
|
* To avoid synchronization among routers booting simultaneously, set
|
||
|
* the hello timer to a random value between 1 to PIM_TIMER_HELLO_PERIOD.
|
||
|
*/
|
||
|
random_delay = 1 + (random() % (long)(PIM_TIMER_HELLO_PERIOD - 1));
|
||
|
v->uv_pim_hello_timer = random_delay;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Stop a vif (either physical interface or tunnel).
|
||
|
* If we are running only PIM we don't have tunnels.
|
||
|
*/
|
||
|
static void
|
||
|
stop_vif(vifi)
|
||
|
vifi_t vifi;
|
||
|
{
|
||
|
struct uvif *v;
|
||
|
struct listaddr *a;
|
||
|
register pim_nbr_entry_t *n, *next;
|
||
|
struct vif_acl *acl;
|
||
|
|
||
|
/*
|
||
|
* TODO: make sure that the kernel viftable is
|
||
|
* consistent with the daemon table
|
||
|
*/
|
||
|
v = &uvifs[vifi];
|
||
|
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;
|
||
|
v->uv_pim_hello_timer = 0;
|
||
|
v->uv_gq_timer = 0;
|
||
|
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;
|
||
|
log(LOG_INFO, 0,
|
||
|
"%s goes down; if #%u out of service", v->uv_name, vifi);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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 = 1;
|
||
|
/* TODO: Check all potential interfaces!!! */
|
||
|
/* Check the physical interfaces only */
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||
|
if (v->uv_flags & VIFF_DISABLED)
|
||
|
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; if #%u taken out of service",
|
||
|
v->uv_name, vifi);
|
||
|
stop_vif(vifi);
|
||
|
vifs_down = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
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))
|
||
|
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))
|
||
|
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))
|
||
|
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);
|
||
|
}
|