diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 72e6d4bdb9..ece080347d 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -47,6 +47,7 @@ #include "isisd/isis_spf.h" #include "isisd/isis_events.h" #include "isisd/isis_mt.h" +#include "isisd/isis_tlvs.h" extern struct isis *isis; @@ -78,6 +79,7 @@ struct isis_adjacency *isis_new_adj(const u_char *id, const u_char *snpa, adj->level = level; adj->flaps = 0; adj->last_flap = time(NULL); + adj->threeway_state = ISIS_THREEWAY_DOWN; if (circuit->circ_type == CIRCUIT_T_BROADCAST) { listnode_add(circuit->u.bc.adjdb[level - 1], adj); adj->dischanges[level - 1] = 0; @@ -161,6 +163,51 @@ static const char *adj_state2string(int state) return NULL; /* not reached */ } +void isis_adj_process_threeway(struct isis_adjacency *adj, + struct isis_threeway_adj *tw_adj, + enum isis_adj_usage adj_usage) +{ + enum isis_threeway_state next_tw_state = ISIS_THREEWAY_DOWN; + + if (tw_adj) { + if (tw_adj->state == ISIS_THREEWAY_DOWN) { + next_tw_state = ISIS_THREEWAY_INITIALIZING; + } else if (tw_adj->state == ISIS_THREEWAY_INITIALIZING) { + next_tw_state = ISIS_THREEWAY_UP; + } else if (tw_adj->state == ISIS_THREEWAY_UP) { + if (adj->threeway_state == ISIS_THREEWAY_DOWN) + next_tw_state = ISIS_THREEWAY_DOWN; + else + next_tw_state = ISIS_THREEWAY_UP; + } + } else { + next_tw_state = ISIS_THREEWAY_UP; + } + + if (next_tw_state != adj->threeway_state) { + if (isis->debugs & DEBUG_ADJ_PACKETS) { + zlog_info("ISIS-Adj (%s): Threeway state change %s to %s", + adj->circuit->area->area_tag, + isis_threeway_state_name(adj->threeway_state), + isis_threeway_state_name(next_tw_state)); + } + } + + if (next_tw_state == ISIS_THREEWAY_DOWN) { + isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Neighbor restarted"); + return; + } + + if (next_tw_state == ISIS_THREEWAY_UP) { + if (adj->adj_state != ISIS_ADJ_UP) { + isis_adj_state_change(adj, ISIS_ADJ_UP, NULL); + adj->adj_usage = adj_usage; + } + } + + adj->threeway_state = next_tw_state; +} + void isis_adj_state_change(struct isis_adjacency *adj, enum isis_adj_state new_state, const char *reason) { diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 98bb9838fa..2c3bd19af8 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -25,6 +25,8 @@ #ifndef _ZEBRA_ISIS_ADJACENCY_H #define _ZEBRA_ISIS_ADJACENCY_H +#include "isisd/isis_tlvs.h" + enum isis_adj_usage { ISIS_ADJ_NONE, ISIS_ADJ_LEVEL1, @@ -91,6 +93,8 @@ struct isis_adjacency { u_int16_t hold_time; /* entryRemainingTime */ u_int32_t last_upd; u_int32_t last_flap; /* last time the adj flapped */ + enum isis_threeway_state threeway_state; + uint32_t ext_circuit_id; int flaps; /* number of adjacency flaps */ struct thread *t_expire; /* expire after hold_time */ struct isis_circuit *circuit; /* back pointer */ @@ -98,12 +102,17 @@ struct isis_adjacency { unsigned int mt_count; /* Number of entries in mt_set */ }; +struct isis_threeway_adj; + struct isis_adjacency *isis_adj_lookup(const u_char *sysid, struct list *adjdb); struct isis_adjacency *isis_adj_lookup_snpa(const u_char *ssnpa, struct list *adjdb); struct isis_adjacency *isis_new_adj(const u_char *id, const u_char *snpa, int level, struct isis_circuit *circuit); void isis_delete_adj(void *adj); +void isis_adj_process_threeway(struct isis_adjacency *adj, + struct isis_threeway_adj *tw_adj, + enum isis_adj_usage adj_usage); void isis_adj_state_change(struct isis_adjacency *adj, enum isis_adj_state state, const char *reason); void isis_adj_print(struct isis_adjacency *adj); diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 20a491ac18..8dd43258fd 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -120,6 +120,32 @@ struct iih_info { static int process_p2p_hello(struct iih_info *iih) { + struct isis_threeway_adj *tw_adj = iih->tlvs->threeway_adj; + if (tw_adj) { + if (tw_adj->state > ISIS_THREEWAY_DOWN) { + if (isis->debugs & DEBUG_ADJ_PACKETS) { + zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s) with invalid three-way state: %d\n", + iih->circuit->area->area_tag, + iih->circuit->interface->name, + tw_adj->state); + } + return ISIS_WARNING; + } + + if (tw_adj->neighbor_set + && (memcmp(tw_adj->neighbor_id, isis->sysid, ISIS_SYS_ID_LEN) + || tw_adj->neighbor_circuit_id != (uint32_t) iih->circuit->idx)) { + + if (isis->debugs & DEBUG_ADJ_PACKETS) { + zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s) which lists IS/Circuit different from us as neighbor.\n", + iih->circuit->area->area_tag, + iih->circuit->interface->name); + } + + return ISIS_WARNING; + } + } + /* * My interpertation of the ISO, if no adj exists we will create one for * the circuit @@ -155,6 +181,9 @@ static int process_p2p_hello(struct iih_info *iih) adj->sys_type = ISIS_SYSTYPE_UNKNOWN; } + if (tw_adj && adj->threeway_state == ISIS_THREEWAY_DOWN) + adj->ext_circuit_id = tw_adj->local_circuit_id; + /* 8.2.6 Monitoring point-to-point adjacencies */ adj->hold_time = iih->holdtime; adj->last_upd = time(NULL); @@ -183,14 +212,10 @@ static int process_p2p_hello(struct iih_info *iih) switch (iih->circ_type) { case IS_LEVEL_1: case IS_LEVEL_1_AND_2: - if (adj->adj_state != ISIS_ADJ_UP) { - /* (4) adj state up */ - isis_adj_state_change(adj, ISIS_ADJ_UP, - NULL); - /* (5) adj usage level 1 */ - adj->adj_usage = ISIS_ADJ_LEVEL1; - } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { - ; /* accept */ + if (adj->adj_state != ISIS_ADJ_UP + || adj->adj_usage == ISIS_ADJ_LEVEL1) { + isis_adj_process_threeway(adj, tw_adj, + ISIS_ADJ_LEVEL1); } break; case IS_LEVEL_2: @@ -213,14 +238,10 @@ static int process_p2p_hello(struct iih_info *iih) if (iih->circuit->area->is_type == IS_LEVEL_1_AND_2) { switch (iih->circ_type) { case IS_LEVEL_1: - if (adj->adj_state != ISIS_ADJ_UP) { - /* (6) adj state up */ - isis_adj_state_change(adj, ISIS_ADJ_UP, - NULL); - /* (7) adj usage level 1 */ - adj->adj_usage = ISIS_ADJ_LEVEL1; - } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { - ; /* accept */ + if (adj->adj_state != ISIS_ADJ_UP + || adj->adj_usage == ISIS_ADJ_LEVEL1) { + isis_adj_process_threeway(adj, tw_adj, + ISIS_ADJ_LEVEL1); } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || (adj->adj_usage @@ -232,12 +253,10 @@ static int process_p2p_hello(struct iih_info *iih) } break; case IS_LEVEL_2: - if (adj->adj_state != ISIS_ADJ_UP) { - /* (6) adj state up */ - isis_adj_state_change(adj, ISIS_ADJ_UP, - NULL); - /* (9) adj usage level 2 */ - adj->adj_usage = ISIS_ADJ_LEVEL2; + if (adj->adj_state != ISIS_ADJ_UP + || adj->adj_usage == ISIS_ADJ_LEVEL2) { + isis_adj_process_threeway(adj, tw_adj, + ISIS_ADJ_LEVEL2); } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) || (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)) { @@ -245,17 +264,13 @@ static int process_p2p_hello(struct iih_info *iih) isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Wrong System"); - } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) { - ; /* Accept */ } break; case IS_LEVEL_1_AND_2: - if (adj->adj_state != ISIS_ADJ_UP) { - /* (6) adj state up */ - isis_adj_state_change(adj, ISIS_ADJ_UP, - NULL); - /* (10) adj usage level 1 */ - adj->adj_usage = ISIS_ADJ_LEVEL1AND2; + if (adj->adj_state != ISIS_ADJ_UP + || adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { + isis_adj_process_threeway(adj, tw_adj, + ISIS_ADJ_LEVEL1AND2); } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { @@ -263,9 +278,6 @@ static int process_p2p_hello(struct iih_info *iih) isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Wrong System"); - } else if (adj->adj_usage - == ISIS_ADJ_LEVEL1AND2) { - ; /* Accept */ } break; } @@ -292,20 +304,16 @@ static int process_p2p_hello(struct iih_info *iih) break; case IS_LEVEL_1_AND_2: case IS_LEVEL_2: - if (adj->adj_state != ISIS_ADJ_UP) { - /* (7) adj state up */ - isis_adj_state_change(adj, ISIS_ADJ_UP, - NULL); - /* (8) adj usage level 2 */ - adj->adj_usage = ISIS_ADJ_LEVEL2; + if (adj->adj_state != ISIS_ADJ_UP + || adj->adj_usage == ISIS_ADJ_LEVEL2) { + isis_adj_process_threeway(adj, tw_adj, + ISIS_ADJ_LEVEL2); } else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { /* (6) down - wrong system */ isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Wrong System"); - } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) { - ; /* Accept */ } break; } @@ -350,12 +358,10 @@ static int process_p2p_hello(struct iih_info *iih) break; case IS_LEVEL_1_AND_2: case IS_LEVEL_2: - if (adj->adj_state != ISIS_ADJ_UP) { - /* (8) adj state up */ - isis_adj_state_change(adj, ISIS_ADJ_UP, - NULL); - /* (9) adj usage level 2 */ - adj->adj_usage = ISIS_ADJ_LEVEL2; + if (adj->adj_state != ISIS_ADJ_UP + || adj->adj_usage == ISIS_ADJ_LEVEL2) { + isis_adj_process_threeway(adj, tw_adj, + ISIS_ADJ_LEVEL2); } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (7) down - wrong system */ isis_adj_state_change(adj, @@ -374,8 +380,6 @@ static int process_p2p_hello(struct iih_info *iih) adj, ISIS_ADJ_DOWN, "Area Mismatch"); } - } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) { - ; /* Accept */ } break; } @@ -1552,9 +1556,24 @@ int send_hello(struct isis_circuit *circuit, int level) isis_tlvs_add_area_addresses(tlvs, circuit->area->area_addrs); - if (circuit->circ_type == CIRCUIT_T_BROADCAST) + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { isis_tlvs_add_lan_neighbors( tlvs, circuit->u.bc.lan_neighs[level - 1]); + } else if (circuit->circ_type == CIRCUIT_T_P2P) { + uint32_t ext_circuit_id = circuit->idx; + if (circuit->u.p2p.neighbor) { + isis_tlvs_add_threeway_adj(tlvs, + circuit->u.p2p.neighbor->threeway_state, + ext_circuit_id, + circuit->u.p2p.neighbor->sysid, + circuit->u.p2p.neighbor->ext_circuit_id); + } else { + isis_tlvs_add_threeway_adj(tlvs, + ISIS_THREEWAY_DOWN, + ext_circuit_id, + NULL, 0); + } + } isis_tlvs_set_protocols_supported(tlvs, &circuit->nlpids);