File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / pimdd / timer.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jun 12 07:58:55 2017 UTC (6 years, 11 months ago) by misho
Branches: pimdd, MAIN
CVS tags: v0_2_1p0, v0_2_1, HEAD
pimdd-dense 0.2.1.0_2

/*
 *  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)
 *
 *  $Id: timer.c,v 1.1.1.1 2017/06/12 07:58:55 misho Exp $
 */
/*
 * 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"


/*
 * Global variables
 */

/*
 * Local functions definitions.
 */


/*
 * Local variables
 */
u_int16 unicast_routing_timer;    /* Used to check periodically for any
				   * change in the unicast routing.
				   */
u_int16 unicast_routing_check_interval;
u_int8  ucast_flag;               /* Used to indicate there was a timeout */


/* to request and compare any route changes */
srcentry_t srcentry_save;

/*
 * Init some timers
 */
void
init_timers()
{
    unicast_routing_check_interval = UCAST_ROUTING_CHECK_INTERVAL;
    SET_TIMER(unicast_routing_timer, unicast_routing_check_interval);

    /* Initialize the srcentry used to save the old routes
     * during unicast routing change discovery process.
     */
    srcentry_save.prev       = (srcentry_t *)NULL;
    srcentry_save.next       = (srcentry_t *)NULL;
    srcentry_save.address    = INADDR_ANY_N;
    srcentry_save.mrtlink    = (mrtentry_t *)NULL;
    srcentry_save.incoming   = NO_VIF;
    srcentry_save.upstream   = (pim_nbr_entry_t *)NULL;
    srcentry_save.metric     = ~0;
    srcentry_save.preference = ~0;
    RESET_TIMER(srcentry_save.timer);

}


/*
 * On every timer interrupt, advance (i.e. decrease) the timer for each
 * neighbor and group entry for each vif.
 */
void
age_vifs()
{
    vifi_t vifi;
    register struct uvif *v;
    register pim_nbr_entry_t *next_nbr, *curr_nbr;

/* XXX: TODO: currently, sending to qe* interface which is DOWN
 * doesn't return error (ENETDOWN) on my Solaris machine,
 * so have to check periodically the 
 * interfaces status. If this is fixed, just remove the defs around
 * the "if (vifs_down)" line.
 */

#if (!((defined SunOS) && (SunOS >= 50)))
    if (vifs_down)
#endif /* Solaris */
	check_vif_state();

    /* Age many things */
    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
	if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN))
	    continue;
	/* Timeout neighbors */
	for (curr_nbr = v->uv_pim_neighbors; curr_nbr != NULL;
	     curr_nbr = next_nbr) {
	    next_nbr = curr_nbr->next;
	    /*
	     * Never timeout neighbors with holdtime = 0xffff.
	     * This may be used with ISDN lines to avoid keeping the
	     * link up with periodic Hello messages.
	     */
	    /* TODO: XXX: TIMER implem. dependency! */
	    if (PIM_MESSAGE_HELLO_HOLDTIME_FOREVER == curr_nbr->timer)
		continue;
	    IF_NOT_TIMEOUT(curr_nbr->timer)
		continue;

	    delete_pim_nbr(curr_nbr);
	}
	
	/* PIM_HELLO periodic */
	IF_TIMEOUT(v->uv_pim_hello_timer)
	    send_pim_hello(v, PIM_TIMER_HELLO_HOLDTIME);

	/* IGMP query periodic */
	IF_TIMEOUT(v->uv_gq_timer)
	    query_groups(v);
    }

    IF_DEBUG(DEBUG_IF)
	dump_vifs(stderr);
}


/*
 * Scan the whole routing table and timeout a bunch of timers:
 *  - prune timers
 *  - Join/Prune delay timer
 *  - routing entry
 *  - Assert timer
 */
void
age_routes()
{
    mrtentry_t *mrtentry_ptr, *mrtentry_next;
    grpentry_t *grpentry_ptr, *grpentry_next;
    vifi_t  vifi;
    int change_flag, state_change;
    int update_src_iif;
    u_long curr_bytecnt;

    /*
     * Timing out of the global `unicast_routing_timer` and data rate timer
     */
   IF_TIMEOUT(unicast_routing_timer) {
	ucast_flag = TRUE;
	SET_TIMER(unicast_routing_timer, unicast_routing_check_interval);
    }
    ELSE {
	ucast_flag = FALSE;
    }

    /* Walk the the (S,G) entries */
    if(grplist == (grpentry_t *)NULL) 
	return;
    for(grpentry_ptr = grplist;
	grpentry_ptr != (grpentry_t *)NULL; 
	grpentry_ptr = grpentry_next) {
	grpentry_next = grpentry_ptr->next;

	for(mrtentry_ptr = grpentry_ptr->mrtlink; 
	    mrtentry_ptr != (mrtentry_t *)NULL; 
	    mrtentry_ptr = mrtentry_next) {

	    int mrtentry_is_timedout;
	    u_int16 mrtentry_timeout;
	    u_int16 prune_timeout;
	    u_int16 min_prune_timeout;

	    mrtentry_next = mrtentry_ptr->grpnext;

	    /* Age the entry timer */
	    /* TODO: XXX: TIMER implem. dependency! */
	    COPY_TIMER(mrtentry_ptr->timer, mrtentry_timeout);
	    mrtentry_is_timedout = TIMEOUT(mrtentry_ptr->timer);
	  
	    /* Refresh entry timer if data forwarded */
	    curr_bytecnt = mrtentry_ptr->sg_count.bytecnt;
	    if (k_get_sg_cnt(udp_socket,
			     mrtentry_ptr->source->address,
			     mrtentry_ptr->group->group,
			     &mrtentry_ptr->sg_count)) {
		/* No such routing entry in kernel */
		delete_mrtentry(mrtentry_ptr);
		continue;
	    }
	    if(!(VIFM_ISEMPTY(mrtentry_ptr->oifs)) && 
	       curr_bytecnt != mrtentry_ptr->sg_count.bytecnt) {
		/* Packets have been forwarded - refresh timer
		 * Note that these counters count packets received, 
		 * not packets forwarded.  So only refresh if packets
		 * received and non-null oiflist.
		 */
		IF_DEBUG(DEBUG_MFC)
		    log(LOG_DEBUG, 0, 
			"Refreshing src %s, dst %s after %d bytes forwarded",
			inet_fmt(mrtentry_ptr->source->address, s1), 
			inet_fmt(mrtentry_ptr->group->group, s2), 
			mrtentry_ptr->sg_count.bytecnt); 
		SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT);
		mrtentry_is_timedout = FALSE;
	    }	    

	    /* Time out asserts */
	    if(mrtentry_ptr->flags & MRTF_ASSERTED) 
		IF_TIMEOUT(mrtentry_ptr->assert_timer) {
		    mrtentry_ptr->flags &= ~MRTF_ASSERTED;
		    mrtentry_ptr->upstream = mrtentry_ptr->source->upstream;
		    mrtentry_ptr->metric   = mrtentry_ptr->source->metric;
		    mrtentry_ptr->preference = mrtentry_ptr->source->preference;
	        }

	    /* Time out Pruned interfaces */
	    change_flag = FALSE;
	    min_prune_timeout = 0x7fff;
	    for (vifi = 0; vifi < numvifs; vifi++) {
		COPY_TIMER(mrtentry_ptr->prune_timers[vifi], prune_timeout);
		if (VIFM_ISSET(vifi, mrtentry_ptr->pruned_oifs))
		    IF_TIMEOUT(mrtentry_ptr->prune_timers[vifi]) {
		        /* TODO: XXX: TIMER implem. dependency! */
		        if(prune_timeout < min_prune_timeout) 
			    min_prune_timeout = prune_timeout;
		        VIFM_CLR(vifi, mrtentry_ptr->pruned_oifs);
			RESET_TIMER(mrtentry_ptr->prune_timers[vifi]);
		        change_flag = TRUE;
		    }
	    }
	    
	    /* Unicast Route changes */
	    update_src_iif = FALSE;
	    if (ucast_flag == TRUE) {
		/* iif toward the source */
		srcentry_save.incoming = mrtentry_ptr->source->incoming;
		srcentry_save.upstream = mrtentry_ptr->source->upstream;
		srcentry_save.preference = mrtentry_ptr->source->preference;
		srcentry_save.metric = mrtentry_ptr->source->metric;
		
		if (set_incoming(mrtentry_ptr->source,
				 PIM_IIF_SOURCE) != TRUE) {
		    /*
		     * XXX: not in the spec!
		     * Cannot find route toward that source.
		     * This is bad. Delete the entry.
		     */
		    delete_mrtentry(mrtentry_ptr);
		    continue;
		}
		else {
		    /* iif info found */
		    if (!(mrtentry_ptr->flags & MRTF_ASSERTED) && 
			((srcentry_save.incoming !=
			  mrtentry_ptr->incoming)
			 || (srcentry_save.upstream !=
			     mrtentry_ptr->upstream))) {
			/* Route change has occur */
			update_src_iif = TRUE;
			mrtentry_ptr->incoming =
			    mrtentry_ptr->source->incoming;
			mrtentry_ptr->upstream =
			    mrtentry_ptr->source->upstream;
			/* mrtentry should have pref/metric of upstream
			 * assert winner, but we dont have that info,
			 * so use the source pref/metric, which will be
			 * larger and thus the correct assert winner
			 * from upstream will be chosen.
			 */
			mrtentry_ptr->preference = 
			    mrtentry_ptr->source->preference;
			mrtentry_ptr->metric = 
			    mrtentry_ptr->source->metric;
		    }
		}
	    }
	    
	    /* Do the interface changes if the mrt is still alive
	     * or if a prune timed out before the mrt
	     */
	    /* TODO: XXX: TIMER implem. dependency! */
	    if ((mrtentry_is_timedout &&
		 min_prune_timeout <= mrtentry_timeout)
		|| 
		(!mrtentry_is_timedout && 
		 (change_flag == TRUE || update_src_iif == TRUE))) {
		/* Flush the changes */
		state_change = 
		    change_interfaces(mrtentry_ptr,
				      mrtentry_ptr->incoming,
				      mrtentry_ptr->pruned_oifs,
				      mrtentry_ptr->leaves);
		if(state_change == -1)
		    trigger_prune_alert(mrtentry_ptr);
		if(state_change == 1) 
		    trigger_join_alert(mrtentry_ptr);
	    }
	    
	    /* Time out the entry */
	    IF_TIMER_NOT_SET(mrtentry_ptr->timer) {
		delete_mrtentry(mrtentry_ptr);
		continue;
	    }
	}
    }

    IF_DEBUG(DEBUG_PIM_MRT)
	dump_pim_mrt(stderr);
    return;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>