mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-31 05:25:56 +00:00 
			
		
		
		
	 5047884528
			
		
	
	
		5047884528
		
	
	
	
	
		
			
			Replace all lib/thread cancel macros, use thread_cancel() everywhere. Only the THREAD_OFF macro and thread_cancel() api are supported. Also adjust thread_cancel_async() to NULL caller's pointer (if present). Signed-off-by: Mark Stapp <mjs@voltanet.io>
		
			
				
	
	
		
			328 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			328 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * IS-IS Rout(e)ing protocol - isis_dr.c
 | |
|  *                             IS-IS designated router related routines
 | |
|  *
 | |
|  * Copyright (C) 2001,2002   Sampo Saaristo
 | |
|  *                           Tampere University of Technology
 | |
|  *                           Institute of Communications Engineering
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms of the GNU General Public Licenseas 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; see the file COPYING; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include <zebra.h>
 | |
| 
 | |
| #include "log.h"
 | |
| #include "hash.h"
 | |
| #include "thread.h"
 | |
| #include "linklist.h"
 | |
| #include "vty.h"
 | |
| #include "stream.h"
 | |
| #include "if.h"
 | |
| 
 | |
| #include "isisd/isis_constants.h"
 | |
| #include "isisd/isis_common.h"
 | |
| #include "isisd/isis_misc.h"
 | |
| #include "isisd/isis_flags.h"
 | |
| #include "isisd/isis_circuit.h"
 | |
| #include "isisd/isisd.h"
 | |
| #include "isisd/isis_adjacency.h"
 | |
| #include "isisd/isis_constants.h"
 | |
| #include "isisd/isis_pdu.h"
 | |
| #include "isisd/isis_lsp.h"
 | |
| #include "isisd/isis_dr.h"
 | |
| #include "isisd/isis_events.h"
 | |
| 
 | |
| const char *isis_disflag2string(int disflag)
 | |
| {
 | |
| 
 | |
| 	switch (disflag) {
 | |
| 	case ISIS_IS_NOT_DIS:
 | |
| 		return "is not DIS";
 | |
| 	case ISIS_IS_DIS:
 | |
| 		return "is DIS";
 | |
| 	case ISIS_WAS_DIS:
 | |
| 		return "was DIS";
 | |
| 	default:
 | |
| 		return "unknown DIS state";
 | |
| 	}
 | |
| 	return NULL; /* not reached */
 | |
| }
 | |
| 
 | |
| int isis_run_dr(struct thread *thread)
 | |
| {
 | |
| 	struct isis_circuit_arg *arg = THREAD_ARG(thread);
 | |
| 
 | |
| 	assert(arg);
 | |
| 
 | |
| 	struct isis_circuit *circuit = arg->circuit;
 | |
| 	int level = arg->level;
 | |
| 
 | |
| 	assert(circuit);
 | |
| 
 | |
| 	if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
 | |
| 		zlog_warn("%s: scheduled for non broadcast circuit from %s:%d",
 | |
| 			  __func__, thread->schedfrom, thread->schedfrom_line);
 | |
| 		return ISIS_WARNING;
 | |
| 	}
 | |
| 
 | |
| 	if (circuit->u.bc.run_dr_elect[level - 1])
 | |
| 		zlog_warn("isis_run_dr(): run_dr_elect already set for l%d", level);
 | |
| 
 | |
| 	circuit->u.bc.t_run_dr[level - 1] = NULL;
 | |
| 	circuit->u.bc.run_dr_elect[level - 1] = 1;
 | |
| 
 | |
| 	return ISIS_OK;
 | |
| }
 | |
| 
 | |
| static int isis_check_dr_change(struct isis_adjacency *adj, int level)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	if (adj->dis_record[level - 1].dis
 | |
| 	    != adj->dis_record[(1 * ISIS_LEVELS) + level - 1].dis)
 | |
| 	/* was there a DIS state transition ? */
 | |
| 	{
 | |
| 		adj->dischanges[level - 1]++;
 | |
| 		/* ok rotate the history list through */
 | |
| 		for (i = DIS_RECORDS - 1; i > 0; i--) {
 | |
| 			adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
 | |
| 				adj->dis_record[((i - 1) * ISIS_LEVELS) + level
 | |
| 						- 1]
 | |
| 					.dis;
 | |
| 			adj->dis_record[(i * ISIS_LEVELS) + level - 1]
 | |
| 				.last_dis_change =
 | |
| 				adj->dis_record[((i - 1) * ISIS_LEVELS) + level
 | |
| 						- 1]
 | |
| 					.last_dis_change;
 | |
| 		}
 | |
| 	}
 | |
| 	return ISIS_OK;
 | |
| }
 | |
| 
 | |
| int isis_dr_elect(struct isis_circuit *circuit, int level)
 | |
| {
 | |
| 	struct list *adjdb;
 | |
| 	struct listnode *node;
 | |
| 	struct isis_adjacency *adj, *adj_dr = NULL;
 | |
| 	struct list *list = list_new();
 | |
| 	uint8_t own_prio;
 | |
| 	int biggest_prio = -1;
 | |
| 	int cmp_res, retval = ISIS_OK;
 | |
| 
 | |
| 	own_prio = circuit->priority[level - 1];
 | |
| 	adjdb = circuit->u.bc.adjdb[level - 1];
 | |
| 
 | |
| 	if (!adjdb) {
 | |
| 		zlog_warn("isis_dr_elect() adjdb == NULL");
 | |
| 		list_delete(&list);
 | |
| 		return ISIS_WARNING;
 | |
| 	}
 | |
| 	isis_adj_build_up_list(adjdb, list);
 | |
| 
 | |
| 	/*
 | |
| 	 * Loop the adjacencies and find the one with the biggest priority
 | |
| 	 */
 | |
| 	for (ALL_LIST_ELEMENTS_RO(list, node, adj)) {
 | |
| 		/* clear flag for show output */
 | |
| 		adj->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
 | |
| 		adj->dis_record[level - 1].last_dis_change = time(NULL);
 | |
| 
 | |
| 		if (adj->prio[level - 1] > biggest_prio) {
 | |
| 			biggest_prio = adj->prio[level - 1];
 | |
| 			adj_dr = adj;
 | |
| 		} else if (adj->prio[level - 1] == biggest_prio) {
 | |
| 			/*
 | |
| 			 * Comparison of MACs breaks a tie
 | |
| 			 */
 | |
| 			if (adj_dr) {
 | |
| 				cmp_res = memcmp(adj_dr->snpa, adj->snpa,
 | |
| 						 ETH_ALEN);
 | |
| 				if (cmp_res < 0) {
 | |
| 					adj_dr = adj;
 | |
| 				}
 | |
| 				if (cmp_res == 0)
 | |
| 					zlog_warn(
 | |
| 						"isis_dr_elect(): multiple adjacencies with same SNPA");
 | |
| 			} else {
 | |
| 				adj_dr = adj;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!adj_dr) {
 | |
| 		/*
 | |
| 		 * Could not find the DR - means we are alone. Resign if we were
 | |
| 		 * DR.
 | |
| 		 */
 | |
| 		if (circuit->u.bc.is_dr[level - 1])
 | |
| 			retval = isis_dr_resign(circuit, level);
 | |
| 		list_delete(&list);
 | |
| 		return retval;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Now we have the DR adjacency, compare it to self
 | |
| 	 */
 | |
| 	if (adj_dr->prio[level - 1] < own_prio
 | |
| 	    || (adj_dr->prio[level - 1] == own_prio
 | |
| 		&& memcmp(adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0)) {
 | |
| 		adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
 | |
| 		adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
 | |
| 
 | |
| 		/* rotate the history log */
 | |
| 		for (ALL_LIST_ELEMENTS_RO(list, node, adj))
 | |
| 			isis_check_dr_change(adj, level);
 | |
| 
 | |
| 		/* We are the DR, commence DR */
 | |
| 		if (circuit->u.bc.is_dr[level - 1] == 0 && listcount(list) > 0)
 | |
| 			retval = isis_dr_commence(circuit, level);
 | |
| 	} else {
 | |
| 		/* ok we have found the DIS - lets mark the adjacency */
 | |
| 		/* set flag for show output */
 | |
| 		adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS;
 | |
| 		adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
 | |
| 
 | |
| 		/* now loop through a second time to check if there has been a
 | |
| 		 * DIS change
 | |
| 		 * if yes rotate the history log
 | |
| 		 */
 | |
| 
 | |
| 		for (ALL_LIST_ELEMENTS_RO(list, node, adj))
 | |
| 			isis_check_dr_change(adj, level);
 | |
| 
 | |
| 		/*
 | |
| 		 * We are not DR - if we were -> resign
 | |
| 		 */
 | |
| 		if (circuit->u.bc.is_dr[level - 1])
 | |
| 			retval = isis_dr_resign(circuit, level);
 | |
| 	}
 | |
| 	list_delete(&list);
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| int isis_dr_resign(struct isis_circuit *circuit, int level)
 | |
| {
 | |
| 	uint8_t id[ISIS_SYS_ID_LEN + 2];
 | |
| 
 | |
| 	zlog_debug("isis_dr_resign l%d", level);
 | |
| 
 | |
| 	circuit->u.bc.is_dr[level - 1] = 0;
 | |
| 	circuit->u.bc.run_dr_elect[level - 1] = 0;
 | |
| 	thread_cancel(&circuit->u.bc.t_run_dr[level - 1]);
 | |
| 	thread_cancel(&circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
 | |
| 	circuit->lsp_regenerate_pending[level - 1] = 0;
 | |
| 
 | |
| 	memcpy(id, circuit->isis->sysid, ISIS_SYS_ID_LEN);
 | |
| 	LSP_PSEUDO_ID(id) = circuit->circuit_id;
 | |
| 	LSP_FRAGMENT(id) = 0;
 | |
| 	lsp_purge_pseudo(id, circuit, level);
 | |
| 
 | |
| 	if (level == 1) {
 | |
| 		memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
 | |
| 
 | |
| 		thread_add_timer(master, send_l1_psnp, circuit,
 | |
| 				 isis_jitter(circuit->psnp_interval[level - 1],
 | |
| 					     PSNP_JITTER),
 | |
| 				 &circuit->t_send_psnp[0]);
 | |
| 	} else {
 | |
| 		memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
 | |
| 
 | |
| 		thread_add_timer(master, send_l2_psnp, circuit,
 | |
| 				 isis_jitter(circuit->psnp_interval[level - 1],
 | |
| 					     PSNP_JITTER),
 | |
| 				 &circuit->t_send_psnp[1]);
 | |
| 	}
 | |
| 
 | |
| 	thread_cancel(&circuit->t_send_csnp[level - 1]);
 | |
| 
 | |
| 	thread_add_timer(master, isis_run_dr,
 | |
| 			 &circuit->level_arg[level - 1],
 | |
| 			 2 * circuit->hello_interval[level - 1],
 | |
| 			 &circuit->u.bc.t_run_dr[level - 1]);
 | |
| 
 | |
| 
 | |
| 	thread_add_event(master, isis_event_dis_status_change, circuit, 0,
 | |
| 			 NULL);
 | |
| 
 | |
| 	return ISIS_OK;
 | |
| }
 | |
| 
 | |
| int isis_dr_commence(struct isis_circuit *circuit, int level)
 | |
| {
 | |
| 	uint8_t old_dr[ISIS_SYS_ID_LEN + 2];
 | |
| 
 | |
| 	if (IS_DEBUG_EVENTS)
 | |
| 		zlog_debug("isis_dr_commence l%d", level);
 | |
| 
 | |
| 	/* Lets keep a pause in DR election */
 | |
| 	circuit->u.bc.run_dr_elect[level - 1] = 0;
 | |
| 	circuit->u.bc.is_dr[level - 1] = 1;
 | |
| 
 | |
| 	if (level == 1) {
 | |
| 		memcpy(old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
 | |
| 		LSP_FRAGMENT(old_dr) = 0;
 | |
| 		if (LSP_PSEUDO_ID(old_dr)) {
 | |
| 			/* there was a dr elected, purge its LSPs from the db */
 | |
| 			lsp_purge_pseudo(old_dr, circuit, level);
 | |
| 		}
 | |
| 		memcpy(circuit->u.bc.l1_desig_is, circuit->isis->sysid,
 | |
| 		       ISIS_SYS_ID_LEN);
 | |
| 		*(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) =
 | |
| 			circuit->circuit_id;
 | |
| 
 | |
| 		assert(circuit->circuit_id); /* must be non-zero */
 | |
| 		/*    if (circuit->t_send_l1_psnp)
 | |
| 		   thread_cancel (circuit->t_send_l1_psnp); */
 | |
| 		lsp_generate_pseudo(circuit, 1);
 | |
| 
 | |
| 		thread_add_timer(master, send_l1_csnp, circuit,
 | |
| 				 isis_jitter(circuit->csnp_interval[level - 1],
 | |
| 					     CSNP_JITTER),
 | |
| 				 &circuit->t_send_csnp[0]);
 | |
| 
 | |
| 	} else {
 | |
| 		memcpy(old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
 | |
| 		LSP_FRAGMENT(old_dr) = 0;
 | |
| 		if (LSP_PSEUDO_ID(old_dr)) {
 | |
| 			/* there was a dr elected, purge its LSPs from the db */
 | |
| 			lsp_purge_pseudo(old_dr, circuit, level);
 | |
| 		}
 | |
| 		memcpy(circuit->u.bc.l2_desig_is, circuit->isis->sysid,
 | |
| 		       ISIS_SYS_ID_LEN);
 | |
| 		*(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) =
 | |
| 			circuit->circuit_id;
 | |
| 
 | |
| 		assert(circuit->circuit_id); /* must be non-zero */
 | |
| 		/*    if (circuit->t_send_l1_psnp)
 | |
| 		   thread_cancel (circuit->t_send_l1_psnp); */
 | |
| 		lsp_generate_pseudo(circuit, 2);
 | |
| 
 | |
| 		thread_add_timer(master, send_l2_csnp, circuit,
 | |
| 				 isis_jitter(circuit->csnp_interval[level - 1],
 | |
| 					     CSNP_JITTER),
 | |
| 				 &circuit->t_send_csnp[1]);
 | |
| 	}
 | |
| 
 | |
| 	thread_add_timer(master, isis_run_dr,
 | |
| 			 &circuit->level_arg[level - 1],
 | |
| 			 2 * circuit->hello_interval[level - 1],
 | |
| 			 &circuit->u.bc.t_run_dr[level - 1]);
 | |
| 	thread_add_event(master, isis_event_dis_status_change, circuit, 0,
 | |
| 			 NULL);
 | |
| 
 | |
| 	return ISIS_OK;
 | |
| }
 |