804 lines
24 KiB
C
804 lines
24 KiB
C
/* $NetBSD: mrt.c,v 1.6 2000/12/04 07:05:48 itojun Exp $ */
|
|
/* $KAME: mrt.c,v 1.4 2000/12/04 06:33:10 itojun Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1998 by the University of Oregon.
|
|
* 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 Oregon.
|
|
* The name of the University of Oregon may not be used to endorse or
|
|
* promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THE UNIVERSITY OF OREGON 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 UO, 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
|
|
* Kurt Windisch (kurtw@antc.uoregon.edu)
|
|
*/
|
|
/*
|
|
* Part of this program has been derived from PIM sparse-mode pimd.
|
|
* The pimd program is covered by the license in the accompanying file
|
|
* named "LICENSE.pimd".
|
|
*
|
|
* The pimd program is COPYRIGHT 1998 by University of Southern California.
|
|
*
|
|
* 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 "defs.h"
|
|
|
|
srcentry_t *srclist;
|
|
grpentry_t *grplist;
|
|
|
|
/*
|
|
* Local functions definition
|
|
*/
|
|
static srcentry_t *create_srcentry __P((struct sockaddr_in6 *source));
|
|
static int search_srclist __P((struct sockaddr_in6 *source,
|
|
srcentry_t **sourceEntry));
|
|
static int search_srcmrtlink __P((srcentry_t *srcentry_ptr,
|
|
struct sockaddr_in6 *group,
|
|
mrtentry_t **mrtPtr));
|
|
static void insert_srcmrtlink __P((mrtentry_t *elementPtr,
|
|
mrtentry_t *insertPtr,
|
|
srcentry_t *srcListPtr));
|
|
static grpentry_t *create_grpentry __P((struct sockaddr_in6 *group));
|
|
static int search_grplist __P((struct sockaddr_in6 *group,
|
|
grpentry_t **groupEntry));
|
|
static int search_grpmrtlink __P((grpentry_t *grpentry_ptr,
|
|
struct sockaddr_in6 *source,
|
|
mrtentry_t **mrtPtr));
|
|
static void insert_grpmrtlink __P((mrtentry_t *elementPtr,
|
|
mrtentry_t *insertPtr,
|
|
grpentry_t *grpListPtr));
|
|
static mrtentry_t *alloc_mrtentry __P((srcentry_t *srcentry_ptr,
|
|
grpentry_t *grpentry_ptr));
|
|
static mrtentry_t *create_mrtentry __P((srcentry_t *srcentry_ptr,
|
|
grpentry_t *grpentry_ptr,
|
|
u_int16 flags));
|
|
|
|
|
|
void
|
|
init_pim6_mrt()
|
|
{
|
|
|
|
/* TODO: delete any existing routing table */
|
|
|
|
/* Initialize the source list */
|
|
/* The first entry has the unspecified address and is not used */
|
|
/* The order is the smallest address first. */
|
|
srclist = (srcentry_t *)malloc(sizeof(srcentry_t));
|
|
srclist->next = (srcentry_t *)NULL;
|
|
srclist->prev = (srcentry_t *)NULL;
|
|
memset(&srclist->address, 0, sizeof(struct sockaddr_in6));
|
|
srclist->address.sin6_len = sizeof(struct sockaddr_in6);
|
|
srclist->address.sin6_family = AF_INET6;
|
|
srclist->mrtlink = (mrtentry_t *)NULL;
|
|
srclist->incoming = NO_VIF;
|
|
srclist->upstream = (pim_nbr_entry_t *)NULL;
|
|
srclist->metric = 0;
|
|
srclist->preference = 0;
|
|
srclist->timer = 0;
|
|
|
|
/* Initialize the group list */
|
|
/* The first entry has the unspecified address and is not used */
|
|
/* The order is the smallest address first. */
|
|
grplist = (grpentry_t *)malloc(sizeof(grpentry_t));
|
|
grplist->next = (grpentry_t *)NULL;
|
|
grplist->prev = (grpentry_t *)NULL;
|
|
memset(&grplist->group, 0, sizeof(struct sockaddr_in6));
|
|
grplist->group.sin6_len = sizeof(struct sockaddr_in6);
|
|
grplist->group.sin6_family = AF_INET6;
|
|
grplist->mrtlink = (mrtentry_t *)NULL;
|
|
}
|
|
|
|
|
|
grpentry_t*
|
|
find_group(group)
|
|
struct sockaddr_in6 *group;
|
|
{
|
|
grpentry_t *grpentry_ptr;
|
|
|
|
if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr))
|
|
return (grpentry_t *)NULL;
|
|
|
|
if (search_grplist(group, &grpentry_ptr) == TRUE) {
|
|
/* Group found! */
|
|
return (grpentry_ptr);
|
|
}
|
|
return (grpentry_t *)NULL;
|
|
}
|
|
|
|
|
|
srcentry_t *
|
|
find_source(source)
|
|
struct sockaddr_in6 *source;
|
|
{
|
|
srcentry_t *srcentry_ptr;
|
|
|
|
if (!inet6_valid_host(source))
|
|
return (srcentry_t *)NULL;
|
|
|
|
if (search_srclist(source, &srcentry_ptr) == TRUE) {
|
|
/* Source found! */
|
|
return (srcentry_ptr);
|
|
}
|
|
return (srcentry_t *)NULL;
|
|
}
|
|
|
|
|
|
mrtentry_t *
|
|
find_route(source, group, flags, create)
|
|
struct sockaddr_in6 *source, *group;
|
|
u_int16 flags;
|
|
char create;
|
|
{
|
|
srcentry_t *srcentry_ptr;
|
|
grpentry_t *grpentry_ptr;
|
|
mrtentry_t *mrtentry_ptr;
|
|
|
|
if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr))
|
|
return (mrtentry_t *)NULL;
|
|
|
|
if (!inet6_valid_host(source))
|
|
return (mrtentry_t *)NULL;
|
|
|
|
if (create == DONT_CREATE) {
|
|
if (search_grplist(group, &grpentry_ptr) == FALSE)
|
|
return (mrtentry_t *)NULL;
|
|
/* Search for the source */
|
|
if (search_grpmrtlink(grpentry_ptr, source,
|
|
&mrtentry_ptr) == TRUE) {
|
|
/* Exact (S,G) entry found */
|
|
return (mrtentry_ptr);
|
|
}
|
|
return (mrtentry_t *)NULL;
|
|
}
|
|
|
|
|
|
/* Creation allowed */
|
|
|
|
grpentry_ptr = create_grpentry(group);
|
|
if (grpentry_ptr == (grpentry_t *)NULL) {
|
|
return (mrtentry_t *)NULL;
|
|
}
|
|
|
|
/* Setup the (S,G) routing entry */
|
|
srcentry_ptr = create_srcentry(source);
|
|
if (srcentry_ptr == (srcentry_t *)NULL) {
|
|
if (grpentry_ptr->mrtlink == (mrtentry_t *)NULL) {
|
|
/* New created grpentry. Delete it. */
|
|
delete_grpentry(grpentry_ptr);
|
|
}
|
|
return (mrtentry_t *)NULL;
|
|
}
|
|
|
|
mrtentry_ptr = create_mrtentry(srcentry_ptr, grpentry_ptr, MRTF_SG);
|
|
if (mrtentry_ptr == (mrtentry_t *)NULL) {
|
|
if (grpentry_ptr->mrtlink == (mrtentry_t *)NULL) {
|
|
/* New created grpentry. Delete it. */
|
|
delete_grpentry(grpentry_ptr);
|
|
}
|
|
if (srcentry_ptr->mrtlink == (mrtentry_t *)NULL) {
|
|
/* New created srcentry. Delete it. */
|
|
delete_srcentry(srcentry_ptr);
|
|
}
|
|
return (mrtentry_t *)NULL;
|
|
}
|
|
|
|
if (mrtentry_ptr->flags & MRTF_NEW) {
|
|
struct mrtfilter *f;
|
|
/* The mrtentry pref/metric should be the pref/metric of the
|
|
* _upstream_ assert winner. Since this isn't known now,
|
|
* set it to the config'ed default
|
|
*/
|
|
mrtentry_ptr->incoming = srcentry_ptr->incoming;
|
|
mrtentry_ptr->upstream = srcentry_ptr->upstream;
|
|
mrtentry_ptr->metric = srcentry_ptr->metric;
|
|
mrtentry_ptr->preference = srcentry_ptr->preference;
|
|
|
|
if ((f = search_filter(&group->sin6_addr)))
|
|
IF_COPY(&f->ifset, &mrtentry_ptr->filter_oifs);
|
|
}
|
|
|
|
return (mrtentry_ptr);
|
|
}
|
|
|
|
|
|
void
|
|
delete_srcentry(srcentry_ptr)
|
|
srcentry_t *srcentry_ptr;
|
|
{
|
|
mrtentry_t *mrtentry_ptr;
|
|
mrtentry_t *mrtentry_next;
|
|
|
|
if (srcentry_ptr == (srcentry_t *)NULL)
|
|
return;
|
|
/* TODO: XXX: the first entry is unused and always there */
|
|
srcentry_ptr->prev->next = srcentry_ptr->next;
|
|
if (srcentry_ptr->next != (srcentry_t *)NULL)
|
|
srcentry_ptr->next->prev = srcentry_ptr->prev;
|
|
|
|
for (mrtentry_ptr = srcentry_ptr->mrtlink;
|
|
mrtentry_ptr != (mrtentry_t *)NULL;
|
|
mrtentry_ptr = mrtentry_next) {
|
|
mrtentry_next = mrtentry_ptr->srcnext;
|
|
if (mrtentry_ptr->grpprev != (mrtentry_t *)NULL)
|
|
mrtentry_ptr->grpprev->grpnext = mrtentry_ptr->grpnext;
|
|
else {
|
|
mrtentry_ptr->group->mrtlink = mrtentry_ptr->grpnext;
|
|
if (mrtentry_ptr->grpnext == (mrtentry_t *)NULL) {
|
|
/* Delete the group entry */
|
|
delete_grpentry(mrtentry_ptr->group);
|
|
}
|
|
}
|
|
if (mrtentry_ptr->grpnext != (mrtentry_t *)NULL)
|
|
mrtentry_ptr->grpnext->grpprev = mrtentry_ptr->grpprev;
|
|
FREE_MRTENTRY(mrtentry_ptr);
|
|
}
|
|
free((char *)srcentry_ptr);
|
|
}
|
|
|
|
|
|
void
|
|
delete_grpentry(grpentry_ptr)
|
|
grpentry_t *grpentry_ptr;
|
|
{
|
|
mrtentry_t *mrtentry_ptr;
|
|
mrtentry_t *mrtentry_next;
|
|
|
|
if (grpentry_ptr == (grpentry_t *)NULL)
|
|
return;
|
|
/* TODO: XXX: the first entry is unused and always there */
|
|
grpentry_ptr->prev->next = grpentry_ptr->next;
|
|
if (grpentry_ptr->next != (grpentry_t *)NULL)
|
|
grpentry_ptr->next->prev = grpentry_ptr->prev;
|
|
|
|
for (mrtentry_ptr = grpentry_ptr->mrtlink;
|
|
mrtentry_ptr != (mrtentry_t *)NULL;
|
|
mrtentry_ptr = mrtentry_next) {
|
|
mrtentry_next = mrtentry_ptr->grpnext;
|
|
if (mrtentry_ptr->srcprev != (mrtentry_t *)NULL)
|
|
mrtentry_ptr->srcprev->srcnext = mrtentry_ptr->srcnext;
|
|
else {
|
|
mrtentry_ptr->source->mrtlink = mrtentry_ptr->srcnext;
|
|
if (mrtentry_ptr->srcnext == (mrtentry_t *)NULL) {
|
|
/* Delete the srcentry if this was the last routing entry */
|
|
delete_srcentry(mrtentry_ptr->source);
|
|
}
|
|
}
|
|
if (mrtentry_ptr->srcnext != (mrtentry_t *)NULL)
|
|
mrtentry_ptr->srcnext->srcprev = mrtentry_ptr->srcprev;
|
|
FREE_MRTENTRY(mrtentry_ptr);
|
|
}
|
|
free((char *)grpentry_ptr);
|
|
}
|
|
|
|
|
|
void
|
|
delete_mrtentry(mrtentry_ptr)
|
|
mrtentry_t *mrtentry_ptr;
|
|
{
|
|
if (mrtentry_ptr == (mrtentry_t *)NULL)
|
|
return;
|
|
|
|
/* Delete the kernel cache first */
|
|
k_del_mfc(mld6_socket, &mrtentry_ptr->source->address,
|
|
&mrtentry_ptr->group->group);
|
|
|
|
#ifdef RSRR
|
|
/* Tell the reservation daemon */
|
|
rsrr_cache_clean(mrtentry_ptr);
|
|
#endif /* RSRR */
|
|
|
|
/* (S,G) mrtentry */
|
|
/* Delete from the grpentry MRT chain */
|
|
if (mrtentry_ptr->grpprev != (mrtentry_t *)NULL)
|
|
mrtentry_ptr->grpprev->grpnext = mrtentry_ptr->grpnext;
|
|
else {
|
|
mrtentry_ptr->group->mrtlink = mrtentry_ptr->grpnext;
|
|
if (mrtentry_ptr->grpnext == (mrtentry_t *)NULL) {
|
|
/* Delete the group entry */
|
|
delete_grpentry(mrtentry_ptr->group);
|
|
}
|
|
}
|
|
|
|
if (mrtentry_ptr->grpnext != (mrtentry_t *)NULL)
|
|
mrtentry_ptr->grpnext->grpprev = mrtentry_ptr->grpprev;
|
|
|
|
/* Delete from the srcentry MRT chain */
|
|
if (mrtentry_ptr->srcprev != (mrtentry_t *)NULL)
|
|
mrtentry_ptr->srcprev->srcnext = mrtentry_ptr->srcnext;
|
|
else {
|
|
mrtentry_ptr->source->mrtlink = mrtentry_ptr->srcnext;
|
|
if (mrtentry_ptr->srcnext == (mrtentry_t *)NULL) {
|
|
/* Delete the srcentry if this was the last routing entry */
|
|
delete_srcentry(mrtentry_ptr->source);
|
|
}
|
|
}
|
|
if (mrtentry_ptr->srcnext != (mrtentry_t *)NULL)
|
|
mrtentry_ptr->srcnext->srcprev = mrtentry_ptr->srcprev;
|
|
|
|
FREE_MRTENTRY(mrtentry_ptr);
|
|
}
|
|
|
|
|
|
static int
|
|
search_srclist(source, sourceEntry)
|
|
struct sockaddr_in6 *source;
|
|
register srcentry_t **sourceEntry;
|
|
{
|
|
register srcentry_t *s_prev,*s;
|
|
|
|
for (s_prev = srclist, s = s_prev->next; s != (srcentry_t *)NULL;
|
|
s_prev = s, s = s->next) {
|
|
/* The srclist is ordered with the smallest addresses first.
|
|
* The first entry is not used.
|
|
*/
|
|
if (inet6_lessthan(&s->address, source))
|
|
continue;
|
|
if (inet6_equal(&s->address, source)) {
|
|
*sourceEntry = s;
|
|
return(TRUE);
|
|
}
|
|
break;
|
|
}
|
|
*sourceEntry = s_prev; /* The insertion point is between s_prev and s */
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
static int
|
|
search_grplist(group, groupEntry)
|
|
struct sockaddr_in6 *group;
|
|
register grpentry_t **groupEntry;
|
|
{
|
|
register grpentry_t *g_prev, *g;
|
|
|
|
for (g_prev = grplist, g = g_prev->next; g != (grpentry_t *)NULL;
|
|
g_prev = g, g = g->next) {
|
|
/* The grplist is ordered with the smallest address first.
|
|
* The first entry is not used.
|
|
*/
|
|
if (inet6_lessthan(&g->group, group))
|
|
continue;
|
|
if (inet6_equal(&g->group, group)) {
|
|
*groupEntry = g;
|
|
return(TRUE);
|
|
}
|
|
break;
|
|
}
|
|
*groupEntry = g_prev; /* The insertion point is between g_prev and g */
|
|
return(FALSE);
|
|
}
|
|
|
|
static srcentry_t *
|
|
create_srcentry(source)
|
|
struct sockaddr_in6 *source;
|
|
{
|
|
register srcentry_t *srcentry_ptr;
|
|
srcentry_t *srcentry_prev;
|
|
|
|
if (search_srclist(source, &srcentry_prev) == TRUE)
|
|
return (srcentry_prev);
|
|
|
|
srcentry_ptr = (srcentry_t *)malloc(sizeof(srcentry_t));
|
|
if (srcentry_ptr == (srcentry_t *)NULL) {
|
|
log(LOG_WARNING, 0, "Memory allocation error for srcentry %s",
|
|
inet6_fmt(&source->sin6_addr));
|
|
return (srcentry_t *)NULL;
|
|
}
|
|
|
|
srcentry_ptr->address = *source;
|
|
/*
|
|
* Free the memory if there is error getting the iif and
|
|
* the next hop (upstream) router.
|
|
*/
|
|
if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) {
|
|
free((char *)srcentry_ptr);
|
|
return (srcentry_t *)NULL;
|
|
}
|
|
srcentry_ptr->mrtlink = (mrtentry_t *)NULL;
|
|
srcentry_ptr->timer = 0;
|
|
srcentry_ptr->next = srcentry_prev->next;
|
|
srcentry_prev->next = srcentry_ptr;
|
|
srcentry_ptr->prev = srcentry_prev;
|
|
if (srcentry_ptr->next != (srcentry_t *)NULL)
|
|
srcentry_ptr->next->prev = srcentry_ptr;
|
|
|
|
IF_DEBUG(DEBUG_MFC)
|
|
log(LOG_DEBUG, 0, "create source entry, source %s",
|
|
inet6_fmt(&source->sin6_addr));
|
|
return (srcentry_ptr);
|
|
}
|
|
|
|
|
|
static grpentry_t *
|
|
create_grpentry(group)
|
|
struct sockaddr_in6 *group;
|
|
{
|
|
register grpentry_t *grpentry_ptr;
|
|
grpentry_t *grpentry_prev;
|
|
|
|
if (search_grplist(group, &grpentry_prev) == TRUE)
|
|
return (grpentry_prev);
|
|
|
|
grpentry_ptr = (grpentry_t *)malloc(sizeof(grpentry_t));
|
|
if (grpentry_ptr == (grpentry_t *)NULL) {
|
|
log(LOG_WARNING, 0, "Memory allocation error for grpentry %s",
|
|
inet6_fmt(&group->sin6_addr));
|
|
return (grpentry_t *)NULL;
|
|
}
|
|
|
|
grpentry_ptr->group = *group;
|
|
grpentry_ptr->mrtlink = (mrtentry_t *)NULL;
|
|
|
|
/* Now it is safe to include the new group entry */
|
|
grpentry_ptr->next = grpentry_prev->next;
|
|
grpentry_prev->next = grpentry_ptr;
|
|
grpentry_ptr->prev = grpentry_prev;
|
|
if (grpentry_ptr->next != (grpentry_t *)NULL)
|
|
grpentry_ptr->next->prev = grpentry_ptr;
|
|
|
|
IF_DEBUG(DEBUG_MFC)
|
|
log(LOG_DEBUG, 0, "create group entry, group %s",
|
|
inet6_fmt(&group->sin6_addr));
|
|
return(grpentry_ptr);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the entry is found and then *mrtPtr is set to point to that
|
|
* entry. Otherwise return FALSE and *mrtPtr points the previous entry
|
|
* (or NULL if first in the chain.
|
|
*/
|
|
static int
|
|
search_srcmrtlink(srcentry_ptr, group, mrtPtr)
|
|
srcentry_t *srcentry_ptr;
|
|
struct sockaddr_in6 *group;
|
|
mrtentry_t **mrtPtr;
|
|
{
|
|
register mrtentry_t *mrtentry_ptr;
|
|
register mrtentry_t *m_prev = (mrtentry_t *)NULL;
|
|
|
|
for(mrtentry_ptr = srcentry_ptr->mrtlink;
|
|
mrtentry_ptr != (mrtentry_t *)NULL;
|
|
m_prev = mrtentry_ptr, mrtentry_ptr = mrtentry_ptr->srcnext) {
|
|
/* The entries are ordered with the smaller group address first.
|
|
* The addresses are in network order.
|
|
*/
|
|
if (inet6_lessthan(&mrtentry_ptr->group->group, group))
|
|
continue;
|
|
if (inet6_equal(&mrtentry_ptr->group->group, group)) {
|
|
*mrtPtr = mrtentry_ptr;
|
|
return(TRUE);
|
|
}
|
|
break;
|
|
}
|
|
*mrtPtr = m_prev;
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if the entry is found and then *mrtPtr is set to point to that
|
|
* entry. Otherwise return FALSE and *mrtPtr points the previous entry
|
|
* (or NULL if first in the chain.
|
|
*/
|
|
static int
|
|
search_grpmrtlink(grpentry_ptr, source, mrtPtr)
|
|
grpentry_t *grpentry_ptr;
|
|
struct sockaddr_in6 *source;
|
|
mrtentry_t **mrtPtr;
|
|
{
|
|
register mrtentry_t *mrtentry_ptr;
|
|
register mrtentry_t *m_prev = (mrtentry_t *)NULL;
|
|
|
|
for (mrtentry_ptr = grpentry_ptr->mrtlink;
|
|
mrtentry_ptr != (mrtentry_t *)NULL;
|
|
m_prev = mrtentry_ptr, mrtentry_ptr = mrtentry_ptr->grpnext) {
|
|
/* The entries are ordered with the smaller source address first.
|
|
* The addresses are in network order.
|
|
*/
|
|
if (inet6_lessthan(&mrtentry_ptr->source->address, source))
|
|
continue;
|
|
if (inet6_equal(source, &mrtentry_ptr->source->address)) {
|
|
*mrtPtr = mrtentry_ptr;
|
|
return(TRUE);
|
|
}
|
|
break;
|
|
}
|
|
*mrtPtr = m_prev;
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
static void
|
|
insert_srcmrtlink(mrtentry_new, mrtentry_prev, srcentry_ptr)
|
|
mrtentry_t *mrtentry_new;
|
|
mrtentry_t *mrtentry_prev;
|
|
srcentry_t *srcentry_ptr;
|
|
{
|
|
if (mrtentry_prev == (mrtentry_t *)NULL) {
|
|
/* Has to be insert as the head entry for this source */
|
|
mrtentry_new->srcnext = srcentry_ptr->mrtlink;
|
|
mrtentry_new->srcprev = (mrtentry_t *)NULL;
|
|
srcentry_ptr->mrtlink = mrtentry_new;
|
|
}
|
|
else {
|
|
/* Insert right after the mrtentry_prev */
|
|
mrtentry_new->srcnext = mrtentry_prev->srcnext;
|
|
mrtentry_new->srcprev = mrtentry_prev;
|
|
mrtentry_prev->srcnext = mrtentry_new;
|
|
}
|
|
if (mrtentry_new->srcnext != (mrtentry_t *)NULL)
|
|
mrtentry_new->srcnext->srcprev = mrtentry_new;
|
|
}
|
|
|
|
|
|
static void
|
|
insert_grpmrtlink(mrtentry_new, mrtentry_prev, grpentry_ptr)
|
|
mrtentry_t *mrtentry_new;
|
|
mrtentry_t *mrtentry_prev;
|
|
grpentry_t *grpentry_ptr;
|
|
{
|
|
if (mrtentry_prev == (mrtentry_t *)NULL) {
|
|
/* Has to be insert as the head entry for this group */
|
|
mrtentry_new->grpnext = grpentry_ptr->mrtlink;
|
|
mrtentry_new->grpprev = (mrtentry_t *)NULL;
|
|
grpentry_ptr->mrtlink = mrtentry_new;
|
|
}
|
|
else {
|
|
/* Insert right after the mrtentry_prev */
|
|
mrtentry_new->grpnext = mrtentry_prev->grpnext;
|
|
mrtentry_new->grpprev = mrtentry_prev;
|
|
mrtentry_prev->grpnext = mrtentry_new;
|
|
}
|
|
if (mrtentry_new->grpnext != (mrtentry_t *)NULL)
|
|
mrtentry_new->grpnext->grpprev = mrtentry_new;
|
|
}
|
|
|
|
|
|
static mrtentry_t *
|
|
alloc_mrtentry(srcentry_ptr, grpentry_ptr)
|
|
srcentry_t *srcentry_ptr;
|
|
grpentry_t *grpentry_ptr;
|
|
{
|
|
register mrtentry_t *mrtentry_ptr;
|
|
u_int16 i, *i_ptr;
|
|
u_long *il_ptr;
|
|
u_int8 vif_numbers;
|
|
|
|
mrtentry_ptr = (mrtentry_t *)malloc(sizeof(mrtentry_t));
|
|
if (mrtentry_ptr == (mrtentry_t *)NULL) {
|
|
log(LOG_WARNING, 0, "alloc_mrtentry(): out of memory");
|
|
return (mrtentry_t *)NULL;
|
|
}
|
|
|
|
/*
|
|
* grpnext, grpprev, srcnext, srcprev will be setup when we link the
|
|
* mrtentry to the source and group chains
|
|
*/
|
|
mrtentry_ptr->source = srcentry_ptr;
|
|
mrtentry_ptr->group = grpentry_ptr;
|
|
mrtentry_ptr->incoming = NO_VIF;
|
|
IF_ZERO(&mrtentry_ptr->leaves);
|
|
IF_ZERO(&mrtentry_ptr->pruned_oifs);
|
|
IF_ZERO(&mrtentry_ptr->oifs);
|
|
IF_ZERO(&mrtentry_ptr->filter_oifs);
|
|
IF_ZERO(&mrtentry_ptr->asserted_oifs);
|
|
mrtentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
|
|
mrtentry_ptr->metric = 0;
|
|
mrtentry_ptr->preference = 0;
|
|
#ifdef RSRR
|
|
mrtentry_ptr->rsrr_cache = (struct rsrr_cache *)NULL;
|
|
#endif /* RSRR */
|
|
|
|
/*
|
|
* XXX: TODO: if we are short in memory, we can reserve as few as possible
|
|
* space for vif timers (per group and/or routing entry), but then everytime
|
|
* when a new interfaces is configured, the router will be restarted and
|
|
* will delete the whole routing table. The "memory is cheap" solution is
|
|
* to reserve timer space for all potential vifs in advance and then no
|
|
* need to delete the routing table and disturb the forwarding.
|
|
*/
|
|
#ifdef SAVE_MEMORY
|
|
mrtentry_ptr->prune_timers = (u_int16 *)malloc(sizeof(u_int16) * numvifs);
|
|
mrtentry_ptr->prune_delay_timerids =
|
|
(u_long *)malloc(sizeof(u_long) * numvifs);
|
|
mrtentry_ptr->last_assert = (u_long *)malloc(sizeof(u_long) * numvifs);
|
|
mrtentry_ptr->last_prune = (u_long *)malloc(sizeof(u_long) * numvifs);
|
|
vif_numbers = numvifs;
|
|
#else
|
|
mrtentry_ptr->prune_timers =
|
|
(u_int16 *)malloc(sizeof(u_int16) * total_interfaces);
|
|
mrtentry_ptr->prune_delay_timerids =
|
|
(u_long *)malloc(sizeof(u_long) * total_interfaces);
|
|
mrtentry_ptr->last_assert =
|
|
(u_long *)malloc(sizeof(u_long) * total_interfaces);
|
|
mrtentry_ptr->last_prune =
|
|
(u_long *)malloc(sizeof(u_long) * total_interfaces);
|
|
vif_numbers = total_interfaces;
|
|
#endif /* SAVE_MEMORY */
|
|
if ((mrtentry_ptr->prune_timers == (u_int16 *)NULL) ||
|
|
(mrtentry_ptr->prune_delay_timerids == (u_long *)NULL) ||
|
|
(mrtentry_ptr->last_assert == (u_long *)NULL) ||
|
|
(mrtentry_ptr->last_prune == (u_long *)NULL)) {
|
|
log(LOG_WARNING, 0, "alloc_mrtentry(): out of memory");
|
|
FREE_MRTENTRY(mrtentry_ptr);
|
|
return (mrtentry_t *)NULL;
|
|
}
|
|
/* Reset the timers */
|
|
for (i = 0, i_ptr = mrtentry_ptr->prune_timers; i < vif_numbers;
|
|
i++, i_ptr++)
|
|
*i_ptr = 0;
|
|
for (i = 0, il_ptr = mrtentry_ptr->prune_delay_timerids; i < vif_numbers;
|
|
i++, il_ptr++)
|
|
*il_ptr = 0;
|
|
for (i = 0, il_ptr = mrtentry_ptr->last_assert; i < vif_numbers;
|
|
i++, il_ptr++)
|
|
*il_ptr = 0;
|
|
for (i = 0, il_ptr = mrtentry_ptr->last_prune; i < vif_numbers;
|
|
i++, il_ptr++)
|
|
*il_ptr = 0;
|
|
|
|
mrtentry_ptr->flags = MRTF_NEW;
|
|
mrtentry_ptr->timer = 0;
|
|
mrtentry_ptr->join_delay_timerid = 0;
|
|
mrtentry_ptr->assert_timer = 0;
|
|
mrtentry_ptr->graft = (pim_graft_entry_t *)NULL;
|
|
|
|
return(mrtentry_ptr);
|
|
}
|
|
|
|
|
|
static mrtentry_t *
|
|
create_mrtentry(srcentry_ptr, grpentry_ptr, flags)
|
|
srcentry_t *srcentry_ptr;
|
|
grpentry_t *grpentry_ptr;
|
|
u_int16 flags;
|
|
{
|
|
mrtentry_t *r_new;
|
|
mrtentry_t *r_grp_insert, *r_src_insert; /* pointers to insert */
|
|
struct sockaddr_in6 *source;
|
|
struct sockaddr_in6 *group;
|
|
|
|
/* (S,G) entry */
|
|
source = &srcentry_ptr->address;
|
|
group = &grpentry_ptr->group;
|
|
|
|
if (search_grpmrtlink(grpentry_ptr, source, &r_grp_insert) == TRUE) {
|
|
return(r_grp_insert);
|
|
}
|
|
if (search_srcmrtlink(srcentry_ptr, group, &r_src_insert) == TRUE) {
|
|
/*
|
|
* Hmmm, search_grpmrtlink() didn't find the entry, but
|
|
* search_srcmrtlink() did find it! Shoudn't happen. Panic!
|
|
*/
|
|
log(LOG_ERR, 0, "MRT inconsistency for src %s and grp %s\n",
|
|
inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr));
|
|
/* not reached but to make lint happy */
|
|
return (mrtentry_t *)NULL;
|
|
}
|
|
/*
|
|
* Create and insert in group mrtlink and source mrtlink chains.
|
|
*/
|
|
r_new = alloc_mrtentry(srcentry_ptr, grpentry_ptr);
|
|
if (r_new == (mrtentry_t *)NULL)
|
|
return (mrtentry_t *)NULL;
|
|
/*
|
|
* r_new has to be insert right after r_grp_insert in the
|
|
* grp mrtlink chain and right after r_src_insert in the
|
|
* src mrtlink chain
|
|
*/
|
|
insert_grpmrtlink(r_new, r_grp_insert, grpentry_ptr);
|
|
insert_srcmrtlink(r_new, r_src_insert, srcentry_ptr);
|
|
r_new->flags |= MRTF_SG;
|
|
return (r_new);
|
|
}
|
|
|
|
/* ======================== */
|
|
/* filter related functions */
|
|
struct mrtfilter *filterlist;
|
|
|
|
/*
|
|
* Search for a filter entry in the filter list.
|
|
*/
|
|
struct mrtfilter *
|
|
search_filter(maddr)
|
|
struct in6_addr *maddr;
|
|
{
|
|
struct mrtfilter *f;
|
|
struct sockaddr_in6 msa6;
|
|
|
|
for (f = filterlist; f; f = f->next) {
|
|
switch(f->type) {
|
|
case FILTER_RANGE:
|
|
msa6.sin6_scope_id = 0; /* XXX: scope consideration */
|
|
msa6.sin6_addr = *maddr;
|
|
if (inet6_greateroreq(&msa6, &f->mrtf_from) &&
|
|
inet6_lessoreq(&msa6, &f->mrtf_to))
|
|
return(f);
|
|
break;
|
|
case FILTER_PREFIX:
|
|
msa6.sin6_scope_id = 0; /* XXX: scope consideration */
|
|
if (inet6_match_prefix(&msa6, &f->mrtf_prefix,
|
|
&f->mrtf_mask))
|
|
return(f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Make a new filter entry.
|
|
* This function assumes
|
|
*/
|
|
struct mrtfilter *
|
|
add_filter(type, maddr1, maddr2, plen)
|
|
struct in6_addr *maddr1, *maddr2;
|
|
int type, plen;
|
|
{
|
|
struct mrtfilter *f;
|
|
struct sockaddr_in6 from, to;
|
|
|
|
if ((f = malloc(sizeof(*f))) == NULL)
|
|
log(LOG_ERR, 0, "add_filter: malloc failed"); /* assert */
|
|
memset((void *)f, 0, sizeof(*f));
|
|
|
|
f->type = type;
|
|
switch(type) {
|
|
case FILTER_RANGE:
|
|
memset((void *)&from, 0, sizeof(from));
|
|
memset((void *)&to, 0, sizeof(to));
|
|
from.sin6_addr = *maddr1;
|
|
to.sin6_addr = *maddr2;
|
|
if (inet6_lessthan(&from, &to)) {
|
|
f->mrtf_from = from;
|
|
f->mrtf_to = to;
|
|
}
|
|
else {
|
|
f->mrtf_from = to;
|
|
f->mrtf_to = from;
|
|
}
|
|
break;
|
|
case FILTER_PREFIX:
|
|
f->mrtf_prefix.sin6_addr = *maddr1;
|
|
MASKLEN_TO_MASK6(plen, f->mrtf_mask);
|
|
break;
|
|
}
|
|
f->next = filterlist;
|
|
filterlist = f;
|
|
|
|
return(f);
|
|
}
|