diff --git a/isisd/fabricd.c b/isisd/fabricd.c new file mode 100644 index 0000000000..d37e6a71d8 --- /dev/null +++ b/isisd/fabricd.c @@ -0,0 +1,136 @@ +/* + * IS-IS Rout(e)ing protocol - OpenFabric extensions + * + * Copyright (C) 2018 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR 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 +#include "isisd/fabricd.h" +#include "isisd/isisd.h" +#include "isisd/isis_memory.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_adjacency.h" + +DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric") + +/* Tracks initial synchronization as per section 2.4 + * + * We declare the sync complete once we have seen at least one + * CSNP and there are no more LSPs with SSN or SRM set. + */ +enum fabricd_sync_state { + FABRICD_SYNC_PENDING, + FABRICD_SYNC_STARTED, + FABRICD_SYNC_COMPLETE +}; + +struct fabricd { + enum fabricd_sync_state initial_sync_state; + time_t initial_sync_start; + struct isis_circuit *initial_sync_circuit; + struct thread *initial_sync_timeout; +}; + +struct fabricd *fabricd_new(void) +{ + struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv)); + + rv->initial_sync_state = FABRICD_SYNC_PENDING; + return rv; +}; + +static int fabricd_initial_sync_timeout(struct thread *thread) +{ + struct fabricd *f = THREAD_ARG(thread); + + zlog_info("OpenFabric: Initial synchronization on %s timed out!", + f->initial_sync_circuit->interface->name); + f->initial_sync_state = FABRICD_SYNC_PENDING; + f->initial_sync_circuit = NULL; + f->initial_sync_timeout = NULL; + return 0; +} + +void fabricd_initial_sync_hello(struct isis_circuit *circuit) +{ + struct fabricd *f = circuit->area->fabricd; + + if (!f) + return; + + if (f->initial_sync_state > FABRICD_SYNC_PENDING) + return; + + f->initial_sync_state = FABRICD_SYNC_STARTED; + + long timeout = 2 * circuit->hello_interval[1] * circuit->hello_multiplier[1]; + + f->initial_sync_circuit = circuit; + if (f->initial_sync_timeout) + return; + + thread_add_timer(master, fabricd_initial_sync_timeout, f, + timeout, &f->initial_sync_timeout); + f->initial_sync_start = monotime(NULL); + + zlog_info("OpenFabric: Started initial synchronization with %s on %s", + sysid_print(circuit->u.p2p.neighbor->sysid), + circuit->interface->name); +} + +bool fabricd_initial_sync_is_in_progress(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return false; + + if (f->initial_sync_state > FABRICD_SYNC_PENDING + && f->initial_sync_state < FABRICD_SYNC_COMPLETE) + return true; + + return false; +} + +struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + if (!f) + return NULL; + + return f->initial_sync_circuit; +} + +void fabricd_initial_sync_finish(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return; + + if (monotime(NULL) - f->initial_sync_start < 5) + return; + + zlog_info("OpenFabric: Initial synchronization on %s complete.", + f->initial_sync_circuit->interface->name); + f->initial_sync_state = FABRICD_SYNC_COMPLETE; + f->initial_sync_circuit = NULL; + thread_cancel(f->initial_sync_timeout); + f->initial_sync_timeout = NULL; +} diff --git a/isisd/fabricd.h b/isisd/fabricd.h new file mode 100644 index 0000000000..35a5bb6a3b --- /dev/null +++ b/isisd/fabricd.h @@ -0,0 +1,36 @@ +/* + * IS-IS Rout(e)ing protocol - OpenFabric extensions + * + * Copyright (C) 2018 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR 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 + */ +#ifndef FABRICD_H +#define FABRICD_H + +struct fabricd; + +struct isis_circuit; +struct isis_area; + +struct fabricd *fabricd_new(void); +void fabricd_initial_sync_hello(struct isis_circuit *circuit); +bool fabricd_initial_sync_is_in_progress(struct isis_area *area); +struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area); +void fabricd_initial_sync_finish(struct isis_area *area); + +#endif diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 4b3d78421e..56dbc25829 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -48,6 +48,7 @@ #include "isisd/isis_events.h" #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" +#include "isisd/fabricd.h" extern struct isis *isis; @@ -193,6 +194,9 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, } } + if (next_tw_state != ISIS_THREEWAY_DOWN) + fabricd_initial_sync_hello(adj->circuit); + if (next_tw_state == ISIS_THREEWAY_DOWN) { isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Neighbor restarted"); return; @@ -306,10 +310,15 @@ void isis_adj_state_change(struct isis_adjacency *adj, adj->last_flap = time(NULL); adj->flaps++; - /* 7.3.17 - going up on P2P -> send CSNP */ - /* FIXME: yup, I know its wrong... but i will do - * it! (for now) */ - send_csnp(circuit, level); + if (level == IS_LEVEL_1) { + thread_add_timer(master, send_l1_csnp, + circuit, 0, + &circuit->t_send_csnp[0]); + } else { + thread_add_timer(master, send_l2_csnp, + circuit, 0, + &circuit->t_send_csnp[1]); + } } else if (new_state == ISIS_ADJ_DOWN) { if (adj->circuit->u.p2p.neighbor == adj) adj->circuit->u.p2p.neighbor = NULL; diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 66c97ae897..90bcece15d 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -56,6 +56,7 @@ #include "isisd/isis_te.h" #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" +#include "isisd/fabricd.h" static int lsp_l1_refresh(struct thread *thread); static int lsp_l2_refresh(struct thread *thread); @@ -1813,6 +1814,7 @@ int lsp_tick(struct thread *thread) int level; uint16_t rem_lifetime; time_t now = monotime(NULL); + bool fabricd_sync_incomplete = false; lsp_list = list_new(); @@ -1821,6 +1823,8 @@ int lsp_tick(struct thread *thread) area->t_tick = NULL; thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); + struct isis_circuit *fabricd_init_c = fabricd_initial_sync_circuit(area); + /* * Build a list of LSPs with (any) SRMflag set * and removed the ones that have aged out @@ -1880,6 +1884,15 @@ int lsp_tick(struct thread *thread) dnode); } else if (flags_any_set(lsp->SRMflags)) listnode_add(lsp_list, lsp); + + if (fabricd_init_c) { + fabricd_sync_incomplete |= + ISIS_CHECK_FLAG(lsp->SSNflags, + fabricd_init_c); + fabricd_sync_incomplete |= + ISIS_CHECK_FLAG(lsp->SRMflags, + fabricd_init_c); + } } /* @@ -1915,6 +1928,9 @@ int lsp_tick(struct thread *thread) } } + if (fabricd_init_c && !fabricd_sync_incomplete) + fabricd_initial_sync_finish(area); + list_delete_and_null(&lsp_list); return ISIS_OK; diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 5c4e3a35bc..8a5db3c6f9 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -56,6 +56,7 @@ #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" #include "isisd/isis_errors.h" +#include "isisd/fabricd.h" static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit, int level) @@ -207,6 +208,12 @@ static int process_p2p_hello(struct iih_info *iih) thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time, &adj->t_expire); + /* While fabricds initial sync is in progress, ignore hellos from other + * interfaces than the one we are performing the initial sync on. */ + if (fabricd_initial_sync_is_in_progress(iih->circuit->area) + && fabricd_initial_sync_circuit(iih->circuit->area) != iih->circuit) + return ISIS_OK; + /* 8.2.5.2 a) a match was detected */ if (isis_tlvs_area_addresses_match(iih->tlvs, iih->circuit->area->area_addrs)) { @@ -1157,7 +1164,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, circuit->u.bc.adjdb[level - 1])) return ISIS_OK; /* Silently discard */ } else { - if (!circuit->u.p2p.neighbor) { + if (!fabricd && !circuit->u.p2p.neighbor) { zlog_warn("no p2p neighbor on circuit %s", circuit->interface->name); return ISIS_OK; /* Silently discard */ @@ -1582,8 +1589,15 @@ int send_hello(struct isis_circuit *circuit, int level) && !circuit->disable_threeway_adj) { uint32_t ext_circuit_id = circuit->idx; if (circuit->u.p2p.neighbor) { + uint8_t threeway_state; + + if (fabricd_initial_sync_is_in_progress(circuit->area) + && fabricd_initial_sync_circuit(circuit->area) != circuit) + threeway_state = ISIS_THREEWAY_DOWN; + else + threeway_state = circuit->u.p2p.neighbor->threeway_state; isis_tlvs_add_threeway_adj(tlvs, - circuit->u.p2p.neighbor->threeway_state, + threeway_state, ext_circuit_id, circuit->u.p2p.neighbor->sysid, circuit->u.p2p.neighbor->ext_circuit_id); @@ -1889,8 +1903,9 @@ int send_l1_csnp(struct thread *thread) circuit->t_send_csnp[0] = NULL; - if (circuit->circ_type == CIRCUIT_T_BROADCAST - && circuit->u.bc.is_dr[0]) { + if ((circuit->circ_type == CIRCUIT_T_BROADCAST + && circuit->u.bc.is_dr[0]) + || circuit->circ_type == CIRCUIT_T_P2P) { send_csnp(circuit, 1); } /* set next timer thread */ @@ -1911,8 +1926,9 @@ int send_l2_csnp(struct thread *thread) circuit->t_send_csnp[1] = NULL; - if (circuit->circ_type == CIRCUIT_T_BROADCAST - && circuit->u.bc.is_dr[1]) { + if ((circuit->circ_type == CIRCUIT_T_BROADCAST + && circuit->u.bc.is_dr[1]) + || circuit->circ_type == CIRCUIT_T_P2P) { send_csnp(circuit, 2); } /* set next timer thread */ diff --git a/isisd/isisd.c b/isisd/isisd.c index 609ca450df..e8efa391e0 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -56,6 +56,7 @@ #include "isisd/isis_events.h" #include "isisd/isis_te.h" #include "isisd/isis_mt.h" +#include "isisd/fabricd.h" struct isis *isis = NULL; @@ -95,6 +96,7 @@ void isis_new(unsigned long process_id) */ /* isis->debugs = 0xFFFF; */ isisMplsTE.status = disable; /* Only support TE metric */ + QOBJ_REG(isis, isis); } @@ -156,6 +158,8 @@ struct isis_area *isis_area_create(const char *area_tag) listnode_add(isis->area_list, area); area->isis = isis; + if (fabricd) + area->fabricd = fabricd_new(); QOBJ_REG(area, isis_area); return area; diff --git a/isisd/isisd.h b/isisd/isisd.h index b76b0b7847..0e75b870a5 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -57,6 +57,8 @@ extern struct zebra_privs_t isisd_privs; /* #define EXTREME_DEBUG */ /* #define EXTREME_DICT_DEBUG */ +struct fabricd; + struct isis { unsigned long process_id; int sysid_set; @@ -111,6 +113,8 @@ struct isis_area { */ int lsp_regenerate_pending[ISIS_LEVELS]; + struct fabricd *fabricd; + /* * Configurables */ diff --git a/isisd/subdir.am b/isisd/subdir.am index 5593f2a4ed..cd8bfdbddc 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -43,6 +43,7 @@ noinst_HEADERS += \ isisd/isis_zebra.h \ isisd/isisd.h \ isisd/iso_checksum.h \ + isisd/fabricd.h \ # end LIBISIS_SOURCES = \ @@ -71,6 +72,7 @@ LIBISIS_SOURCES = \ isisd/isis_zebra.c \ isisd/isisd.c \ isisd/iso_checksum.c \ + isisd/fabricd.c \ # end ISIS_SOURCES = \