Initial revision
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
new file mode 100644
index 0000000..8079bd1
--- /dev/null
+++ b/isisd/isis_adjacency.c
@@ -0,0 +1,508 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_adjacency.c
+ * handling of IS-IS adjacencies
+ *
+ * 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; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <zebra.h>
+#include <net/ethernet.h>
+
+
+#include "log.h"
+#include "memory.h"
+#include "hash.h"
+#include "vty.h"
+#include "linklist.h"
+#include "thread.h"
+#include "if.h"
+#include "stream.h"
+
+#include "isisd/dict.h"
+#include "isisd/include-netbsd/iso.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_dr.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_pdu.h"
+
+
+extern struct isis *isis;
+
+
+struct isis_adjacency *
+adj_alloc (u_char *id)
+{
+ struct isis_adjacency *adj;
+
+ adj = XMALLOC (MTYPE_ISIS_ADJACENCY, sizeof (struct isis_adjacency));
+ memset (adj, 0, sizeof (struct isis_adjacency));
+ memcpy (adj->sysid, id, ISIS_SYS_ID_LEN);
+
+ return adj;
+}
+
+struct isis_adjacency *
+isis_new_adj (u_char *id, u_char *snpa, int level,
+ struct isis_circuit *circuit)
+{
+
+ struct isis_adjacency *adj;
+ int i;
+
+ adj = adj_alloc (id); /* P2P kludge */
+
+ if (adj == NULL){
+ zlog_err ("Out of memory!");
+ return NULL;
+ }
+
+ memcpy (adj->snpa, snpa, 6);
+ adj->circuit = circuit;
+ adj->level = level;
+ adj->flaps = 0;
+ adj->last_flap = time (NULL);
+ if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
+ listnode_add (circuit->u.bc.adjdb[level-1], adj);
+ adj->dischanges[level - 1] = 0;
+ for (i = 0; i < DIS_RECORDS; i++) /* clear N DIS state change records */
+ {
+ adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis
+ = ISIS_UNKNOWN_DIS;
+ adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change
+ = time (NULL);
+ }
+ }
+
+ return adj;
+}
+
+struct isis_adjacency *
+isis_adj_lookup (u_char *sysid, struct list *adjdb)
+{
+ struct isis_adjacency *adj;
+ struct listnode *node;
+
+ for (node = listhead (adjdb); node; nextnode (node)) {
+ adj = getdata (node);
+ if (memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0)
+ return adj;
+ }
+
+ return NULL;
+}
+
+
+struct isis_adjacency *
+isis_adj_lookup_snpa (u_char *ssnpa, struct list *adjdb)
+{
+ struct listnode *node;
+ struct isis_adjacency *adj;
+
+ for (node = listhead (adjdb); node; nextnode (node)) {
+ adj = getdata (node);
+ if (memcmp (adj->snpa, ssnpa, ETH_ALEN) == 0)
+ return adj;
+ }
+
+ return NULL;
+}
+
+/*
+ * When we recieve a NULL list, we will know its p2p
+ */
+void
+isis_delete_adj (struct isis_adjacency *adj, struct list *adjdb)
+{
+ struct isis_adjacency *adj2;
+ struct listnode *node;
+
+ if (adjdb) {
+ for (node = listhead (adjdb); node; nextnode (node)) {
+ adj2 = getdata (node);
+ if (adj2 == adj)
+ break;
+ }
+ listnode_delete (adjdb, node);
+ }
+
+ if (adj->ipv4_addrs)
+ list_delete (adj->ipv4_addrs);
+#ifdef HAVE_IPV6
+ if (adj->ipv6_addrs)
+ list_delete (adj->ipv6_addrs);
+#endif
+ if (adj) {
+ XFREE (MTYPE_ISIS_ADJACENCY,adj);
+ } else {
+ zlog_info ("tried to delete a non-existent adjacency");
+ }
+
+
+
+ return;
+}
+
+void
+isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state state,
+ char *reason)
+
+{
+ int old_state;
+ int level = adj->level;
+ struct isis_circuit *circuit;
+
+ old_state = adj->adj_state;
+ adj->adj_state = state;
+
+ circuit = adj->circuit;
+
+ if (isis->debugs & DEBUG_ADJ_PACKETS) {
+ zlog_info ("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
+ circuit->area->area_tag,
+ old_state,
+ state,
+ reason ? reason : "unspecified");
+ }
+
+ if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
+ if (state == ISIS_ADJ_UP)
+ circuit->upadjcount[level-1]++;
+ if (state == ISIS_ADJ_DOWN) {
+ isis_delete_adj (adj, adj->circuit->u.bc.adjdb[level - 1]);
+ circuit->upadjcount[level-1]--;
+ }
+
+ list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]);
+ isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1],
+ circuit->u.bc.lan_neighs[level - 1]);
+ } else if (state == ISIS_ADJ_UP) { /* p2p interface */
+ if (adj->sys_type == ISIS_SYSTYPE_UNKNOWN)
+ send_hello (circuit, 1);
+
+ /* update counter & timers for debugging purposes */
+ 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,1);
+ send_csnp (circuit,2);
+ } else if (state == ISIS_ADJ_DOWN) { /* p2p interface */
+ adj->circuit->u.p2p.neighbor = NULL;
+ isis_delete_adj (adj, NULL);
+ }
+ return;
+}
+
+
+void
+isis_adj_print (struct isis_adjacency *adj)
+{
+ struct isis_dynhn *dyn;
+ struct listnode *node;
+ struct in_addr *ipv4_addr;
+#ifdef HAVE_IPV6
+ struct in6_addr *ipv6_addr;
+ u_char ip6 [INET6_ADDRSTRLEN];
+#endif /* HAVE_IPV6 */
+
+ if(!adj)
+ return;
+ dyn = dynhn_find_by_id (adj->sysid);
+ if (dyn)
+ zlog_info ("%s", dyn->name.name);
+
+ zlog_info ("SystemId %20s SNPA %s, level %d\nHolding Time %d",
+ adj->sysid ? sysid_print (adj->sysid) : "unknown" ,
+ snpa_print (adj->snpa),
+ adj->level, adj->hold_time);
+ if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0) {
+ zlog_info ("IPv4 Addresses:");
+
+ for (node = listhead (adj->ipv4_addrs); node; nextnode (node)) {
+ ipv4_addr = getdata (node);
+ zlog_info ("%s", inet_ntoa(*ipv4_addr));
+ }
+ }
+
+#ifdef HAVE_IPV6
+ if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0) {
+ zlog_info ("IPv6 Addresses:");
+ for (node = listhead (adj->ipv6_addrs); node; nextnode (node)) {
+ ipv6_addr = getdata (node);
+ inet_ntop (AF_INET6, ipv6_addr, ip6, INET6_ADDRSTRLEN);
+ zlog_info ("%s", ip6);
+ }
+ }
+#endif /* HAVE_IPV6 */
+ zlog_info ("Speaks: %s", nlpid2string(&adj->nlpids));
+
+
+ return;
+}
+
+int
+isis_adj_expire (struct thread *thread)
+{
+ struct isis_adjacency *adj;
+ int level;
+
+ /*
+ * Get the adjacency
+ */
+ adj = THREAD_ARG (thread);
+ assert (adj);
+ level = adj->level;
+
+ /* trigger the adj expire event */
+ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "holding time expired");
+
+ return 0;
+}
+
+const char *
+adj_state2string (int state)
+{
+
+ switch (state) {
+ case ISIS_ADJ_INITIALIZING:
+ return "Initializing";
+ case ISIS_ADJ_UP:
+ return "Up";
+ case ISIS_ADJ_DOWN:
+ return "Down";
+ default:
+ return "Unknown";
+ }
+
+ return NULL; /* not reached */
+}
+
+/*
+ * show clns/isis neighbor (detail)
+ */
+void
+isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail)
+{
+
+#ifdef HAVE_IPV6
+ struct in6_addr *ipv6_addr;
+ u_char ip6 [INET6_ADDRSTRLEN];
+#endif /* HAVE_IPV6 */
+ struct in_addr *ip_addr;
+ time_t now;
+ struct isis_dynhn *dyn;
+ int level;
+ struct listnode *node;
+
+ dyn = dynhn_find_by_id (adj->sysid);
+ if (dyn)
+ vty_out (vty, " %-20s", dyn->name.name);
+ else if (adj->sysid){
+ vty_out (vty, " %-20s", sysid_print (adj->sysid));
+ } else {
+ vty_out (vty, " unknown ");
+ }
+
+ if (detail == ISIS_UI_LEVEL_BRIEF) {
+ if (adj->circuit)
+ vty_out (vty, "%-12s",adj->circuit->interface->name);
+ else
+ vty_out (vty, "NULL circuit!");
+ vty_out (vty, "%-3u", adj->level); /* level */
+ vty_out (vty, "%-13s", adj_state2string (adj->adj_state));
+ now = time (NULL);
+ vty_out (vty, "%-9lu", adj->last_upd + adj->hold_time - now);
+ vty_out (vty, "%-10s", snpa_print (adj->snpa));
+ vty_out (vty, "%s", VTY_NEWLINE);
+ }
+
+ if (detail == ISIS_UI_LEVEL_DETAIL) {
+ level = adj->level;
+ if (adj->circuit)
+ vty_out (vty, "%s Interface: %s",
+ VTY_NEWLINE,
+ adj->circuit->interface->name); /* interface name */
+ else
+ vty_out (vty, "NULL circuit!%s", VTY_NEWLINE);
+ vty_out (vty, ", Level: %u", adj->level); /* level */
+ vty_out (vty, ", State: %s", adj_state2string (adj->adj_state));
+ now = time (NULL);
+ vty_out (vty, ", Expires in %s",
+ time2string (adj->last_upd + adj->hold_time - now));
+ vty_out (vty, "%s Adjacency flaps: %u",
+ VTY_NEWLINE,
+ adj->flaps);
+ vty_out (vty, ", Last: %s ago", time2string(now - adj->last_flap));
+ vty_out (vty, "%s Circuit type: %s",
+ VTY_NEWLINE,
+ circuit_t2string(adj->circuit_t));
+ vty_out (vty, ", Speaks: %s", nlpid2string(&adj->nlpids));
+ vty_out (vty, "%s SNPA: %s",
+ VTY_NEWLINE,
+ snpa_print (adj->snpa));
+ dyn = dynhn_find_by_id (adj->lanid);
+ if (dyn)
+ vty_out (vty, ", LAN id: %s.%02x",
+ dyn->name.name,
+ adj->lanid[ISIS_SYS_ID_LEN]);
+ else
+ vty_out (vty, ", LAN id: %s.%02x",
+ sysid_print (adj->lanid),
+ adj->lanid[ISIS_SYS_ID_LEN]);
+
+ vty_out (vty, "%s Priority: %u",
+ VTY_NEWLINE,
+ adj->prio[adj->level-1]);
+
+ vty_out (vty, ", %s, DIS flaps: %u, Last: %s ago%s",
+ isis_disflag2string(adj->dis_record[ISIS_LEVELS+level-1].dis),
+ adj->dischanges[level-1],
+ time2string (now -
+ (adj->dis_record[ISIS_LEVELS + level - 1].last_dis_change)),
+ VTY_NEWLINE);
+
+ if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0) {
+ vty_out (vty, " IPv4 Addresses:%s", VTY_NEWLINE);
+ for (node = listhead (adj->ipv4_addrs);node; nextnode (node)) {
+ ip_addr = getdata (node);
+ vty_out (vty, " %s%s", inet_ntoa(*ip_addr), VTY_NEWLINE);
+ }
+ }
+#ifdef HAVE_IPV6
+ if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0) {
+ vty_out (vty, " IPv6 Addresses:%s", VTY_NEWLINE);
+ for (node = listhead (adj->ipv6_addrs); node; nextnode (node)) {
+ ipv6_addr = getdata (node);
+ inet_ntop (AF_INET6, ipv6_addr, ip6, INET6_ADDRSTRLEN);
+ vty_out (vty, " %s%s", ip6, VTY_NEWLINE);
+ }
+ }
+#endif /* HAVE_IPV6 */
+ vty_out (vty, "%s", VTY_NEWLINE);
+ }
+ return;
+}
+
+void
+isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty) {
+ isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_BRIEF);
+}
+
+void
+isis_adj_print_vty_detail (struct isis_adjacency *adj, struct vty *vty) {
+ isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_DETAIL);
+}
+
+void
+isis_adj_print_vty_extensive (struct isis_adjacency *adj, struct vty *vty) {
+ isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_EXTENSIVE);
+}
+
+void
+isis_adj_p2p_print_vty (struct isis_adjacency *adj, struct vty *vty)
+{
+ isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_BRIEF);
+}
+
+void
+isis_adj_p2p_print_vty_detail (struct isis_adjacency *adj, struct vty *vty)
+{
+ isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_DETAIL);
+}
+
+void
+isis_adj_p2p_print_vty_extensive (struct isis_adjacency *adj, struct vty *vty)
+{
+ isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_EXTENSIVE);
+}
+
+void
+isis_adjdb_iterate (struct list *adjdb, void (*func)(struct isis_adjacency*,
+ void *), void *arg)
+{
+ struct listnode *node;
+ struct isis_adjacency *adj;
+ for (node = listhead (adjdb); node; nextnode (node)) {
+ adj = getdata (node);
+ (*func)(adj, arg);
+ }
+}
+
+void
+isis_adj_build_neigh_list (struct list *adjdb, struct list *list)
+
+{
+ struct isis_adjacency *adj;
+ struct listnode *node;
+
+
+ if (!list) {
+ zlog_warn ("isis_adj_build_neigh_list(): NULL list");
+ return;
+ }
+
+ for (node = listhead (adjdb); node; nextnode (node)) {
+ adj = getdata (node);
+ if (!adj) {
+ zlog_warn ("isis_adj_build_neigh_list(): NULL adj");
+ return;
+ }
+
+ if ((adj->adj_state == ISIS_ADJ_UP ||
+ adj->adj_state == ISIS_ADJ_INITIALIZING))
+ listnode_add (list, adj->snpa);
+ }
+ return;
+}
+
+void
+isis_adj_build_up_list (struct list *adjdb, struct list *list)
+{
+ struct isis_adjacency *adj;
+ struct listnode *node;
+
+ if (!list) {
+ zlog_warn ("isis_adj_build_up_list(): NULL list");
+ return;
+ }
+
+ for (node = listhead (adjdb); node; nextnode (node)) {
+ adj = getdata (node);
+
+ if (!adj) {
+ zlog_warn ("isis_adj_build_up_list(): NULL adj");
+ return;
+ }
+
+ if (adj->adj_state == ISIS_ADJ_UP)
+ listnode_add (list, adj);
+ }
+
+ return;
+}
+