diff options
author | paul <paul> | 2002-12-13 20:15:29 +0000 |
---|---|---|
committer | paul <paul> | 2002-12-13 20:15:29 +0000 |
commit | 718e3744195351130f4ce7dbe0613f4b3e23df93 (patch) | |
tree | bac2ad39971cd43f31241ef123bd4e470f695ac9 /ospfd/ospf_opaque.c |
Initial revision
Diffstat (limited to 'ospfd/ospf_opaque.c')
-rw-r--r-- | ospfd/ospf_opaque.c | 2392 |
1 files changed, 2392 insertions, 0 deletions
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c new file mode 100644 index 00000000..67c6608b --- /dev/null +++ b/ospfd/ospf_opaque.c @@ -0,0 +1,2392 @@ +/* + * This is an implementation of rfc2370. + * Copyright (C) 2001 KDD R&D Laboratories, Inc. + * http://www.kddlabs.co.jp/ + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/***** MTYPE definitions are not reflected to "memory.h" yet. *****/ +#define MTYPE_OSPF_OPAQUE_FUNCTAB 0 +#define MTYPE_OPAQUE_INFO_PER_TYPE 0 +#define MTYPE_OPAQUE_INFO_PER_ID 0 + +#include <zebra.h> +#ifdef HAVE_OPAQUE_LSA + +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "thread.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" + +/*------------------------------------------------------------------------* + * Followings are initialize/terminate functions for Opaque-LSAs handling. + *------------------------------------------------------------------------*/ + +#ifdef HAVE_OSPF_TE +#include "ospfd/ospf_te.h" +#endif /* HAVE_OSPF_TE */ + +static void ospf_opaque_register_vty (void); +static void ospf_opaque_funclist_init (void); +static void ospf_opaque_funclist_term (void); +static void free_opaque_info_per_type (void *val); +static void free_opaque_info_per_id (void *val); +static int ospf_opaque_lsa_install_hook (struct ospf_lsa *lsa); +static int ospf_opaque_lsa_delete_hook (struct ospf_lsa *lsa); + +void +ospf_opaque_init (void) +{ + ospf_opaque_register_vty (); + ospf_opaque_funclist_init (); + +#ifdef HAVE_OSPF_TE + if (ospf_mpls_te_init () != 0) + exit (1); +#endif /* HAVE_OSPF_TE */ + + return; +} + +void +ospf_opaque_term (void) +{ +#ifdef HAVE_OSPF_TE + ospf_mpls_te_term (); +#endif /* HAVE_OSPF_TE */ + + ospf_opaque_funclist_term (); + return; +} + +int +ospf_opaque_type9_lsa_init (struct ospf_interface *oi) +{ + if (oi->opaque_lsa_self != NULL) + list_delete (oi->opaque_lsa_self); + + oi->opaque_lsa_self = list_new (); + oi->opaque_lsa_self->del = free_opaque_info_per_type; + oi->t_opaque_lsa_self = NULL; + return 0; +} + +void +ospf_opaque_type9_lsa_term (struct ospf_interface *oi) +{ + OSPF_TIMER_OFF (oi->t_opaque_lsa_self); + if (oi->opaque_lsa_self != NULL) + list_delete (oi->opaque_lsa_self); + oi->opaque_lsa_self = NULL; + return; +} + +int +ospf_opaque_type10_lsa_init (struct ospf_area *area) +{ + if (area->opaque_lsa_self != NULL) + list_delete (area->opaque_lsa_self); + + area->opaque_lsa_self = list_new (); + area->opaque_lsa_self->del = free_opaque_info_per_type; + area->t_opaque_lsa_self = NULL; + +#ifdef MONITOR_LSDB_CHANGE + area->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook; + area->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook; +#endif /* MONITOR_LSDB_CHANGE */ + return 0; +} + +void +ospf_opaque_type10_lsa_term (struct ospf_area *area) +{ +#ifdef MONITOR_LSDB_CHANGE + area->lsdb->new_lsa_hook = + area->lsdb->del_lsa_hook = NULL; +#endif /* MONITOR_LSDB_CHANGE */ + + OSPF_TIMER_OFF (area->t_opaque_lsa_self); + if (area->opaque_lsa_self != NULL) + list_delete (area->opaque_lsa_self); + area->opaque_lsa_self = NULL; + return; +} + +int +ospf_opaque_type11_lsa_init (struct ospf *top) +{ + if (top->opaque_lsa_self != NULL) + list_delete (top->opaque_lsa_self); + + top->opaque_lsa_self = list_new (); + top->opaque_lsa_self->del = free_opaque_info_per_type; + top->t_opaque_lsa_self = NULL; + +#ifdef MONITOR_LSDB_CHANGE + top->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook; + top->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook; +#endif /* MONITOR_LSDB_CHANGE */ + return 0; +} + +void +ospf_opaque_type11_lsa_term (struct ospf *top) +{ +#ifdef MONITOR_LSDB_CHANGE + top->lsdb->new_lsa_hook = + top->lsdb->del_lsa_hook = NULL; +#endif /* MONITOR_LSDB_CHANGE */ + + OSPF_TIMER_OFF (top->t_opaque_lsa_self); + if (top->opaque_lsa_self != NULL) + list_delete (top->opaque_lsa_self); + top->opaque_lsa_self = NULL; + return; +} + +static const char * +ospf_opaque_type_name (u_char opaque_type) +{ + const char *name = "Unknown"; + + switch (opaque_type) + { + case OPAQUE_TYPE_WILDCARD: /* This is a special assignment! */ + name = "Wildcard"; + break; + case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA: + name = "Traffic Engineering LSA"; + break; + case OPAQUE_TYPE_SYCAMORE_OPTICAL_TOPOLOGY_DESC: + name = "Sycamore optical topology description"; + break; + case OPAQUE_TYPE_GRACE_LSA: + name = "Grace-LSA"; + break; + default: + if (OPAQUE_TYPE_RANGE_UNASSIGNED (opaque_type)) + name = "Unassigned"; + else if (OPAQUE_TYPE_RANGE_RESERVED (opaque_type)) + name = "Private/Experimental"; + break; + } + return name; +} + +/*------------------------------------------------------------------------* + * Followings are management functions to store user specified callbacks. + *------------------------------------------------------------------------*/ + +struct opaque_info_per_type; /* Forward declaration. */ + +struct ospf_opaque_functab +{ + u_char opaque_type; + struct opaque_info_per_type *oipt; + + int (* new_if_hook)(struct interface *ifp); + int (* del_if_hook)(struct interface *ifp); + void (* ism_change_hook)(struct ospf_interface *oi, int old_status); + void (* nsm_change_hook)(struct ospf_neighbor *nbr, int old_status); + void (* config_write_router)(struct vty *vty); + void (* config_write_if )(struct vty *vty, struct interface *ifp); + void (* config_write_debug )(struct vty *vty); + void (* show_opaque_info )(struct vty *vty, struct ospf_lsa *lsa); + int (* lsa_originator)(void *arg); + void (* lsa_refresher )(struct ospf_lsa *lsa); + int (* new_lsa_hook)(struct ospf_lsa *lsa); + int (* del_lsa_hook)(struct ospf_lsa *lsa); +}; + +static list ospf_opaque_type9_funclist; +static list ospf_opaque_type10_funclist; +static list ospf_opaque_type11_funclist; + +static void +ospf_opaque_del_functab (void *val) +{ + XFREE (MTYPE_OSPF_OPAQUE_FUNCTAB, val); + return; +} + +static void +ospf_opaque_funclist_init (void) +{ + list funclist; + + funclist = ospf_opaque_type9_funclist = list_new (); + funclist->del = ospf_opaque_del_functab; + + funclist = ospf_opaque_type10_funclist = list_new (); + funclist->del = ospf_opaque_del_functab; + + funclist = ospf_opaque_type11_funclist = list_new (); + funclist->del = ospf_opaque_del_functab; + return; +} + +static void +ospf_opaque_funclist_term (void) +{ + list funclist; + + funclist = ospf_opaque_type9_funclist; + list_delete (funclist); + + funclist = ospf_opaque_type10_funclist; + list_delete (funclist); + + funclist = ospf_opaque_type11_funclist; + list_delete (funclist); + return; +} + +static list +ospf_get_opaque_funclist (u_char lsa_type) +{ + list funclist = NULL; + + switch (lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + funclist = ospf_opaque_type9_funclist; + break; + case OSPF_OPAQUE_AREA_LSA: + funclist = ospf_opaque_type10_funclist; + break; + case OSPF_OPAQUE_AS_LSA: + funclist = ospf_opaque_type11_funclist; + break; + default: + zlog_warn ("ospf_get_opaque_funclist: Unexpected LSA-type(%u)", lsa_type); + break; + } + return funclist; +} + +int +ospf_register_opaque_functab ( + u_char lsa_type, + u_char opaque_type, + int (* new_if_hook)(struct interface *ifp), + int (* del_if_hook)(struct interface *ifp), + void (* ism_change_hook)(struct ospf_interface *oi, int old_status), + void (* nsm_change_hook)(struct ospf_neighbor *nbr, int old_status), + void (* config_write_router)(struct vty *vty), + void (* config_write_if )(struct vty *vty, struct interface *ifp), + void (* config_write_debug )(struct vty *vty), + void (* show_opaque_info )(struct vty *vty, struct ospf_lsa *lsa), + int (* lsa_originator)(void *arg), + void (* lsa_refresher )(struct ospf_lsa *lsa), + int (* new_lsa_hook)(struct ospf_lsa *lsa), + int (* del_lsa_hook)(struct ospf_lsa *lsa)) +{ + list funclist; + struct ospf_opaque_functab *new; + int rc = -1; + + if ((funclist = ospf_get_opaque_funclist (lsa_type)) == NULL) + { + zlog_warn ("ospf_register_opaque_functab: Cannot get funclist for Type-%u LSAs?", lsa_type); + goto out; + } + else + { + listnode node; + struct ospf_opaque_functab *functab; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->opaque_type == opaque_type) + { + zlog_warn ("ospf_register_opaque_functab: Duplicated entry?: lsa_type(%u), opaque_type(%u)", lsa_type, opaque_type); + goto out; + } + } + + if ((new = XCALLOC (MTYPE_OSPF_OPAQUE_FUNCTAB, + sizeof (struct ospf_opaque_functab))) == NULL) + { + zlog_warn ("ospf_register_opaque_functab: XMALLOC: %s", strerror (errno)); + goto out; + } + + new->opaque_type = opaque_type; + new->oipt = NULL; + new->new_if_hook = new_if_hook; + new->del_if_hook = del_if_hook; + new->ism_change_hook = ism_change_hook; + new->nsm_change_hook = nsm_change_hook; + new->config_write_router = config_write_router; + new->config_write_if = config_write_if; + new->config_write_debug = config_write_debug; + new->show_opaque_info = show_opaque_info; + new->lsa_originator = lsa_originator; + new->lsa_refresher = lsa_refresher; + new->new_lsa_hook = new_lsa_hook; + new->del_lsa_hook = del_lsa_hook; + + listnode_add (funclist, new); + rc = 0; + +out: + return rc; +} + +void +ospf_delete_opaque_functab (u_char lsa_type, u_char opaque_type) +{ + list funclist; + listnode node; + struct ospf_opaque_functab *functab; + + if ((funclist = ospf_get_opaque_funclist (lsa_type)) != NULL) + for (node = listhead (funclist); node; nextnode (node)) + { + if ((functab = getdata (node)) != NULL + && functab->opaque_type == opaque_type) + { + /* Cleanup internal control information, if it still remains. */ + if (functab->oipt != NULL) + free_opaque_info_per_type (functab->oipt); + + /* Dequeue listnode entry from the list. */ + listnode_delete (funclist, functab); + + /* Avoid misjudgement in the next lookup. */ + if (listcount (funclist) == 0) + funclist->head = funclist->tail = NULL; + + XFREE (MTYPE_OSPF_OPAQUE_FUNCTAB, functab); + goto out; + } + } +out: + return; +} + +static struct ospf_opaque_functab * +ospf_opaque_functab_lookup (struct ospf_lsa *lsa) +{ + list funclist; + listnode node; + struct ospf_opaque_functab *functab; + u_char key = GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)); + + if ((funclist = ospf_get_opaque_funclist (lsa->data->type)) != NULL) + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->opaque_type == key) + return functab; + + return NULL; +} + +/*------------------------------------------------------------------------* + * Followings are management functions for self-originated LSA entries. + *------------------------------------------------------------------------*/ + +/* + * Opaque-LSA control information per opaque-type. + * Single Opaque-Type may have multiple instances; each of them will be + * identified by their opaque-id. + */ +struct opaque_info_per_type +{ + u_char opaque_type; + + enum { PROC_NORMAL, PROC_SUSPEND } status; + + /* + * Thread for (re-)origination scheduling for this opaque-type. + * + * Initial origination of Opaque-LSAs is controlled by generic + * Opaque-LSA handling module so that same opaque-type entries are + * called all at once when certain conditions are met. + * However, there might be cases that some Opaque-LSA clients need + * to (re-)originate their own Opaque-LSAs out-of-sync with others. + * This thread is prepared for that specific purpose. + */ + struct thread *t_opaque_lsa_self; + + /* + * Backpointer to an "owner" which is opaque-type dependent. + * type-9: struct ospf_interface + * type-10: struct ospf_area + * type-11: struct ospf + */ + void *owner; + + /* Collection of callback functions for this opaque-type. */ + struct ospf_opaque_functab *functab; + + /* List of Opaque-LSA control informations per opaque-id. */ + list id_list; +}; + +/* Opaque-LSA control information per opaque-id. */ +struct opaque_info_per_id +{ + u_int32_t opaque_id; + + /* Thread for refresh/flush scheduling for this opaque-type/id. */ + struct thread *t_opaque_lsa_self; + + /* Backpointer to Opaque-LSA control information per opaque-type. */ + struct opaque_info_per_type *opqctl_type; + + /* Here comes an actual Opaque-LSA entry for this opaque-type/id. */ + struct ospf_lsa *lsa; +}; + +static struct opaque_info_per_type *register_opaque_info_per_type (struct ospf_opaque_functab *functab, struct ospf_lsa *new); +static struct opaque_info_per_type *lookup_opaque_info_by_type (struct ospf_lsa *lsa); +static struct opaque_info_per_id *register_opaque_info_per_id (struct opaque_info_per_type *oipt, struct ospf_lsa *new); +static struct opaque_info_per_id *lookup_opaque_info_by_id (struct opaque_info_per_type *oipt, struct ospf_lsa *lsa); +static struct opaque_info_per_id *register_opaque_lsa (struct ospf_lsa *new); + + +static struct opaque_info_per_type * +register_opaque_info_per_type (struct ospf_opaque_functab *functab, + struct ospf_lsa *new) +{ + struct ospf *top; + struct opaque_info_per_type *oipt; + + if ((oipt = XCALLOC (MTYPE_OPAQUE_INFO_PER_TYPE, + sizeof (struct opaque_info_per_type))) == NULL) + { + zlog_warn ("register_opaque_info_per_type: XMALLOC: %s", strerror (errno)); + goto out; + } + + switch (new->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + oipt->owner = new->oi; + listnode_add (new->oi->opaque_lsa_self, oipt); + break; + case OSPF_OPAQUE_AREA_LSA: + oipt->owner = new->area; + listnode_add (new->area->opaque_lsa_self, oipt); + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_top; + if (new->area != NULL && (top = new->area->top) == NULL) + { + free_opaque_info_per_type ((void *) oipt); + oipt = NULL; + goto out; /* This case may not exist. */ + } + oipt->owner = top; + listnode_add (top->opaque_lsa_self, oipt); + break; + default: + free_opaque_info_per_type ((void *) oipt); + oipt = NULL; + goto out; /* This case may not exist. */ + } + + oipt->opaque_type = GET_OPAQUE_TYPE (ntohl (new->data->id.s_addr)); + oipt->status = PROC_NORMAL; + oipt->t_opaque_lsa_self = NULL; + oipt->functab = functab; + functab->oipt = oipt; + oipt->id_list = list_new (); + oipt->id_list->del = free_opaque_info_per_id; + +out: + return oipt; +} + +static void +free_opaque_info_per_type (void *val) +{ + struct opaque_info_per_type *oipt = (struct opaque_info_per_type *) val; + struct opaque_info_per_id *oipi; + struct ospf_lsa *lsa; + listnode node; + + /* Control information per opaque-id may still exist. */ + for (node = listhead (oipt->id_list); node; nextnode (node)) + { + if ((oipi = getdata (node)) == NULL) + continue; + if ((lsa = oipi->lsa) == NULL) + continue; + if (IS_LSA_MAXAGE (lsa)) + continue; + ospf_opaque_lsa_flush_schedule (lsa); + } + + OSPF_TIMER_OFF (oipt->t_opaque_lsa_self); + list_delete (oipt->id_list); + XFREE (MTYPE_OPAQUE_INFO_PER_TYPE, oipt); + return; +} + +static struct opaque_info_per_type * +lookup_opaque_info_by_type (struct ospf_lsa *lsa) +{ + struct ospf *top; + struct ospf_area *area; + struct ospf_interface *oi; + list listtop = NULL; + listnode node; + struct opaque_info_per_type *oipt = NULL; + u_char key = GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)); + + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + if ((oi = lsa->oi) != NULL) + listtop = oi->opaque_lsa_self; + else + zlog_warn ("Type-9 Opaque-LSA: Reference to OI is missing?"); + break; + case OSPF_OPAQUE_AREA_LSA: + if ((area = lsa->area) != NULL) + listtop = area->opaque_lsa_self; + else + zlog_warn ("Type-10 Opaque-LSA: Reference to AREA is missing?"); + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_top; + if ((area = lsa->area) != NULL && (top = area->top) == NULL) + { + zlog_warn ("Type-11 Opaque-LSA: Reference to OSPF is missing?"); + break; /* Unlikely to happen. */ + } + listtop = top->opaque_lsa_self; + break; + default: + zlog_warn ("lookup_opaque_info_by_type: Unexpected LSA-type(%u)", lsa->data->type); + break; + } + + if (listtop != NULL) + for (node = listhead (listtop); node; nextnode (node)) + if ((oipt = getdata (node)) != NULL) + if (oipt->opaque_type == key) + return oipt; + + return NULL; +} + +static struct opaque_info_per_id * +register_opaque_info_per_id (struct opaque_info_per_type *oipt, + struct ospf_lsa *new) +{ + struct opaque_info_per_id *oipi; + + if ((oipi = XCALLOC (MTYPE_OPAQUE_INFO_PER_ID, + sizeof (struct opaque_info_per_id))) == NULL) + { + zlog_warn ("register_opaque_info_per_id: XMALLOC: %s", strerror (errno)); + goto out; + } + oipi->opaque_id = GET_OPAQUE_ID (ntohl (new->data->id.s_addr)); + oipi->t_opaque_lsa_self = NULL; + oipi->opqctl_type = oipt; + oipi->lsa = ospf_lsa_lock (new); + + listnode_add (oipt->id_list, oipi); + +out: + return oipi; +} + +static void +free_opaque_info_per_id (void *val) +{ + struct opaque_info_per_id *oipi = (struct opaque_info_per_id *) val; + + OSPF_TIMER_OFF (oipi->t_opaque_lsa_self); + if (oipi->lsa != NULL) + ospf_lsa_unlock (oipi->lsa); + XFREE (MTYPE_OPAQUE_INFO_PER_ID, oipi); + return; +} + +static struct opaque_info_per_id * +lookup_opaque_info_by_id (struct opaque_info_per_type *oipt, + struct ospf_lsa *lsa) +{ + listnode node; + struct opaque_info_per_id *oipi; + u_int32_t key = GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)); + + for (node = listhead (oipt->id_list); node; nextnode (node)) + if ((oipi = getdata (node)) != NULL) + if (oipi->opaque_id == key) + return oipi; + + return NULL; +} + +static struct opaque_info_per_id * +register_opaque_lsa (struct ospf_lsa *new) +{ + struct ospf_opaque_functab *functab; + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi = NULL; + + if ((functab = ospf_opaque_functab_lookup (new)) == NULL) + goto out; + + if ((oipt = lookup_opaque_info_by_type (new)) == NULL + && (oipt = register_opaque_info_per_type (functab, new)) == NULL) + goto out; + + if ((oipi = register_opaque_info_per_id (oipt, new)) == NULL) + goto out; + +out: + return oipi; +} + +/*------------------------------------------------------------------------* + * Followings are (vty) configuration functions for Opaque-LSAs handling. + *------------------------------------------------------------------------*/ + +DEFUN (capability_opaque, + capability_opaque_cmd, + "capability opaque", + "Enable specific OSPF feature\n" + "Opaque LSA\n") +{ + struct ospf *ospf = (struct ospf *) vty->index; + + /* Turn on the "master switch" of opaque-lsa capability. */ + if (!CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Opaque capability: OFF -> ON"); + + SET_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE); + ospf_renegotiate_optional_capabilities (ospf); + } + return CMD_SUCCESS; +} + +ALIAS (capability_opaque, + ospf_opaque_capable_cmd, + "ospf opaque-lsa", + "OSPF specific commands\n" + "Enable the Opaque-LSA capability (rfc2370)\n") + +DEFUN (no_capability_opaque, + no_capability_opaque_cmd, + "no capability opaque", + NO_STR + "Enable specific OSPF feature\n" + "Opaque LSA\n") +{ + struct ospf *ospf = (struct ospf *) vty->index; + + /* Turn off the "master switch" of opaque-lsa capability. */ + if (CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Opaque capability: ON -> OFF"); + + UNSET_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE); + ospf_renegotiate_optional_capabilities (ospf); + } + return CMD_SUCCESS; +} + +ALIAS (no_capability_opaque, + no_ospf_opaque_capable_cmd, + "no ospf opaque-lsa", + NO_STR + "OSPF specific commands\n" + "Disable the Opaque-LSA capability (rfc2370)\n") + +static void +ospf_opaque_register_vty (void) +{ + install_element (OSPF_NODE, &capability_opaque_cmd); + install_element (OSPF_NODE, &no_capability_opaque_cmd); + install_element (OSPF_NODE, &ospf_opaque_capable_cmd); + install_element (OSPF_NODE, &no_ospf_opaque_capable_cmd); + return; +} + +/*------------------------------------------------------------------------* + * Followings are collection of user-registered function callers. + *------------------------------------------------------------------------*/ + +static int +opaque_lsa_new_if_callback (list funclist, struct interface *ifp) +{ + listnode node; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->new_if_hook != NULL) + if ((* functab->new_if_hook)(ifp) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int +opaque_lsa_del_if_callback (list funclist, struct interface *ifp) +{ + listnode node; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->del_if_hook != NULL) + if ((* functab->del_if_hook)(ifp) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static void +opaque_lsa_ism_change_callback (list funclist, + struct ospf_interface *oi, int old_status) +{ + listnode node; + struct ospf_opaque_functab *functab; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->ism_change_hook != NULL) + (* functab->ism_change_hook)(oi, old_status); + return; +} + +static void +opaque_lsa_nsm_change_callback (list funclist, + struct ospf_neighbor *nbr, int old_status) +{ + listnode node; + struct ospf_opaque_functab *functab; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->nsm_change_hook != NULL) + (* functab->nsm_change_hook)(nbr, old_status); + return; +} + +static void +opaque_lsa_config_write_router_callback (list funclist, struct vty *vty) +{ + listnode node; + struct ospf_opaque_functab *functab; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->config_write_router != NULL) + (* functab->config_write_router)(vty); + return; +} + +static void +opaque_lsa_config_write_if_callback (list funclist, + struct vty *vty, struct interface *ifp) +{ + listnode node; + struct ospf_opaque_functab *functab; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->config_write_if != NULL) + (* functab->config_write_if)(vty, ifp); + return; +} + +static void +opaque_lsa_config_write_debug_callback (list funclist, struct vty *vty) +{ + listnode node; + struct ospf_opaque_functab *functab; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->config_write_debug != NULL) + (* functab->config_write_debug)(vty); + return; +} + +static int +opaque_lsa_originate_callback (list funclist, void *lsa_type_dependent) +{ + listnode node; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->lsa_originator != NULL) + if ((* functab->lsa_originator)(lsa_type_dependent) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int +new_lsa_callback (list funclist, struct ospf_lsa *lsa) +{ + listnode node; + struct ospf_opaque_functab *functab; + int rc = -1; + + /* This function handles ALL types of LSAs, not only opaque ones. */ + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->new_lsa_hook != NULL) + if ((* functab->new_lsa_hook)(lsa) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int +del_lsa_callback (list funclist, struct ospf_lsa *lsa) +{ + listnode node; + struct ospf_opaque_functab *functab; + int rc = -1; + + /* This function handles ALL types of LSAs, not only opaque ones. */ + for (node = listhead (funclist); node; nextnode (node)) + if ((functab = getdata (node)) != NULL) + if (functab->del_lsa_hook != NULL) + if ((* functab->del_lsa_hook)(lsa) != 0) + goto out; + rc = 0; +out: + return rc; +} + +/*------------------------------------------------------------------------* + * Followings are glue functions to call Opaque-LSA specific processing. + *------------------------------------------------------------------------*/ + +int +ospf_opaque_new_if (struct interface *ifp) +{ + list funclist; + int rc = -1; + + funclist = ospf_opaque_type9_funclist; + if (opaque_lsa_new_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (opaque_lsa_new_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (opaque_lsa_new_if_callback (funclist, ifp) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +int +ospf_opaque_del_if (struct interface *ifp) +{ + list funclist; + int rc = -1; + + funclist = ospf_opaque_type9_funclist; + if (opaque_lsa_del_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (opaque_lsa_del_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (opaque_lsa_del_if_callback (funclist, ifp) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +void +ospf_opaque_ism_change (struct ospf_interface *oi, int old_status) +{ + list funclist; + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_ism_change_callback (funclist, oi, old_status); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_ism_change_callback (funclist, oi, old_status); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_ism_change_callback (funclist, oi, old_status); + + return; +} + +void +ospf_opaque_nsm_change (struct ospf_neighbor *nbr, int old_state) +{ + struct ospf *top; + list funclist; + + if ((top = oi_to_top (nbr->oi)) == NULL) + goto out; + + if (old_state != NSM_Full && nbr->state == NSM_Full) + { + if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + { + if (! CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Opaque-LSA: Now get operational!"); + + SET_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT); + } + + ospf_opaque_lsa_originate_schedule (nbr->oi, NULL); + } + } + else + if (old_state == NSM_Full && nbr->state != NSM_Full) + { +#ifdef NOTYET + /* + * If no more opaque-capable full-state neighbor remains in the + * flooding scope which corresponds to Opaque-LSA type, periodic + * LS flooding should be stopped. + */ +#endif /* NOTYET */ + ; + } + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_nsm_change_callback (funclist, nbr, old_state); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_nsm_change_callback (funclist, nbr, old_state); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_nsm_change_callback (funclist, nbr, old_state); + +out: + return; +} + +void +ospf_opaque_config_write_router (struct vty *vty, struct ospf *ospf) +{ + list funclist; + + if (CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE)) + vty_out (vty, " capability opaque%s", VTY_NEWLINE); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_router_callback (funclist, vty); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_router_callback (funclist, vty); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_router_callback (funclist, vty); + + return; +} + +void +ospf_opaque_config_write_if (struct vty *vty, struct interface *ifp) +{ + list funclist; + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_if_callback (funclist, vty, ifp); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_if_callback (funclist, vty, ifp); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_if_callback (funclist, vty, ifp); + + return; +} + +void +ospf_opaque_config_write_debug (struct vty *vty) +{ + list funclist; + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_debug_callback (funclist, vty); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_debug_callback (funclist, vty); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_debug_callback (funclist, vty); + + return; +} + +void +show_opaque_info_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = (struct lsa_header *) lsa->data; + u_int32_t lsid = ntohl (lsah->id.s_addr); + u_char opaque_type = GET_OPAQUE_TYPE (lsid); + u_int32_t opaque_id = GET_OPAQUE_ID (lsid); + struct ospf_opaque_functab *functab; + + /* Switch output functionality by vty address. */ + if (vty != NULL) + { + vty_out (vty, " Opaque-Type %u (%s)%s", opaque_type, ospf_opaque_type_name (opaque_type), VTY_NEWLINE); + vty_out (vty, " Opaque-ID 0x%x%s", opaque_id, VTY_NEWLINE); + + vty_out (vty, " Opaque-Info: %u octets of data%s%s", + ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE, + VALID_OPAQUE_INFO_LEN(lsah) ? "" : "(Invalid length?)", + VTY_NEWLINE); + } + else + { + zlog_info (" Opaque-Type %u (%s)", opaque_type, ospf_opaque_type_name (opaque_type)); + zlog_info (" Opaque-ID 0x%x", opaque_id); + + zlog_info (" Opaque-Info: %u octets of data%s", + ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE, + VALID_OPAQUE_INFO_LEN(lsah) ? "" : "(Invalid length?)"); + } + + /* Call individual output functions. */ + if ((functab = ospf_opaque_functab_lookup (lsa)) != NULL) + if (functab->show_opaque_info != NULL) + (* functab->show_opaque_info)(vty, lsa); + + return; +} + +void +ospf_opaque_lsa_dump (struct stream *s, u_int16_t length) +{ + struct ospf_lsa lsa; + + lsa.data = (struct lsa_header *) STREAM_PNT (s); + show_opaque_info_detail (NULL, &lsa); + return; +} + +static int +ospf_opaque_lsa_install_hook (struct ospf_lsa *lsa) +{ + list funclist; + int rc = -1; + + /* + * Some Opaque-LSA user may want to monitor every LSA installation + * into the LSDB, regardless with target LSA type. + */ + funclist = ospf_opaque_type9_funclist; + if (new_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (new_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (new_lsa_callback (funclist, lsa) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +static int +ospf_opaque_lsa_delete_hook (struct ospf_lsa *lsa) +{ + list funclist; + int rc = -1; + + /* + * Some Opaque-LSA user may want to monitor every LSA deletion + * from the LSDB, regardless with target LSA type. + */ + funclist = ospf_opaque_type9_funclist; + if (del_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (del_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (del_lsa_callback (funclist, lsa) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +/*------------------------------------------------------------------------* + * Followings are Opaque-LSA origination/refresh management functions. + *------------------------------------------------------------------------*/ + +static int ospf_opaque_type9_lsa_originate (struct thread *t); +static int ospf_opaque_type10_lsa_originate (struct thread *t); +static int ospf_opaque_type11_lsa_originate (struct thread *t); +static void ospf_opaque_lsa_reoriginate_resume (list listtop, void *arg); + +void +ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0) +{ + struct ospf *top; + struct ospf_area *area; + listnode node; + struct opaque_info_per_type *oipt; + int delay = 0; + + if ((top = oi_to_top (oi)) == NULL || (area = oi->area) == NULL) + { + zlog_warn ("ospf_opaque_lsa_originate_schedule: Invalid argument?"); + goto out; + } + + /* It may not a right time to schedule origination now. */ + if (! CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("ospf_opaque_lsa_originate_schedule: Not operational."); + goto out; /* This is not an error. */ + } + if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("ospf_opaque_lsa_originate_schedule: Under blockade."); + goto out; /* This is not an error, too. */ + } + + if (delay0 != NULL) + delay = *delay0; + + /* + * There might be some entries that have been waiting for triggering + * of per opaque-type re-origination get resumed. + */ + ospf_opaque_lsa_reoriginate_resume ( oi->opaque_lsa_self, (void *) oi); + ospf_opaque_lsa_reoriginate_resume (area->opaque_lsa_self, (void *) area); + ospf_opaque_lsa_reoriginate_resume ( top->opaque_lsa_self, (void *) top); + + /* + * Now, schedule origination of all Opaque-LSAs per opaque-type. + */ + if (! list_isempty (ospf_opaque_type9_funclist) + && list_isempty (oi->opaque_lsa_self) + && oi->t_opaque_lsa_self == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Schedule Type-9 Opaque-LSA origination in %d sec later.", delay); + oi->t_opaque_lsa_self = + thread_add_timer (master, ospf_opaque_type9_lsa_originate, oi, delay); + delay += OSPF_MIN_LS_INTERVAL; + } + + if (! list_isempty (ospf_opaque_type10_funclist) + && list_isempty (area->opaque_lsa_self) + && area->t_opaque_lsa_self == NULL) + { + /* + * One AREA may contain multiple OIs, but above 2nd and 3rd + * conditions prevent from scheduling the originate function + * again and again. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Schedule Type-10 Opaque-LSA origination in %d sec later.", delay); + area->t_opaque_lsa_self = + thread_add_timer (master, ospf_opaque_type10_lsa_originate, + area, delay); + delay += OSPF_MIN_LS_INTERVAL; + } + + if (! list_isempty (ospf_opaque_type11_funclist) + && list_isempty (top->opaque_lsa_self) + && top->t_opaque_lsa_self == NULL) + { + /* + * One OSPF may contain multiple AREAs, but above 2nd and 3rd + * conditions prevent from scheduling the originate function + * again and again. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Schedule Type-11 Opaque-LSA origination in %d sec later.", delay); + top->t_opaque_lsa_self = + thread_add_timer (master, ospf_opaque_type11_lsa_originate, + top, delay); + delay += OSPF_MIN_LS_INTERVAL; + } + + /* + * Following section treats a special situation that this node's + * opaque capability has changed as "ON -> OFF -> ON". + */ + if (! list_isempty (ospf_opaque_type9_funclist) + && ! list_isempty (oi->opaque_lsa_self)) + { + for (node = listhead (oi->opaque_lsa_self); node; nextnode (node)) + { + if ((oipt = getdata (node)) == NULL /* Something wrong? */ + || oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND /* Cannot originate now. */ + || ! list_isempty (oipt->id_list)) /* Handler is already active. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule ((void *) oi, + OSPF_OPAQUE_LINK_LSA, oipt->opaque_type); + } + } + + if (! list_isempty (ospf_opaque_type10_funclist) + && ! list_isempty (area->opaque_lsa_self)) + { + for (node = listhead (area->opaque_lsa_self); node; nextnode (node)) + { + if ((oipt = getdata (node)) == NULL /* Something wrong? */ + || oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND /* Cannot originate now. */ + || ! list_isempty (oipt->id_list)) /* Handler is already active. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule ((void *) area, + OSPF_OPAQUE_AREA_LSA, oipt->opaque_type); + } + } + + if (! list_isempty (ospf_opaque_type11_funclist) + && ! list_isempty (top->opaque_lsa_self)) + { + for (node = listhead (top->opaque_lsa_self); node; nextnode (node)) + { + if ((oipt = getdata (node)) == NULL /* Something wrong? */ + || oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND /* Cannot originate now. */ + || ! list_isempty (oipt->id_list)) /* Handler is already active. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule ((void *) top, + OSPF_OPAQUE_AS_LSA, oipt->opaque_type); + } + } + + if (delay0 != NULL) + *delay0 = delay; + +out: + return; +} + +static int +ospf_opaque_type9_lsa_originate (struct thread *t) +{ + struct ospf_interface *oi; + int rc; + + oi = THREAD_ARG (t); + oi->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Timer[Type9-LSA]: Originate Opaque-LSAs for OI %s", + IF_NAME (oi)); + + rc = opaque_lsa_originate_callback (ospf_opaque_type9_funclist, oi); + + return rc; +} + +static int +ospf_opaque_type10_lsa_originate (struct thread *t) +{ + struct ospf_area *area; + int rc; + + area = THREAD_ARG (t); + area->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Timer[Type10-LSA]: Originate Opaque-LSAs for Area %s", + inet_ntoa (area->area_id)); + + rc = opaque_lsa_originate_callback (ospf_opaque_type10_funclist, area); + + return rc; +} + +static int +ospf_opaque_type11_lsa_originate (struct thread *t) +{ + struct ospf *top; + int rc; + + top = THREAD_ARG (t); + top->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Timer[Type11-LSA]: Originate AS-External Opaque-LSAs"); + + rc = opaque_lsa_originate_callback (ospf_opaque_type11_funclist, top); + + return rc; +} + +static void +ospf_opaque_lsa_reoriginate_resume (list listtop, void *arg) +{ + listnode node; + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + + if (listtop == NULL) + goto out; + + /* + * Pickup oipt entries those which in SUSPEND status, and give + * them a chance to start re-origination now. + */ + for (node = listhead (listtop); node; nextnode (node)) + { + if ((oipt = getdata (node)) == NULL + || oipt->status != PROC_SUSPEND) + continue; + + oipt->status = PROC_NORMAL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + continue; + + if ((* functab->lsa_originator)(arg) != 0) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_resume: Failed (opaque-type=%u)", oipt->opaque_type); + continue; + } + } + +out: + return; +} + +struct ospf_lsa * +ospf_opaque_lsa_install (struct ospf_lsa *lsa, int rt_recalc) +{ + struct ospf_lsa *new = NULL; + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi; + struct ospf *top; + + /* Don't take "rt_recalc" into consideration for now. *//* XXX */ + + if (! IS_LSA_SELF (lsa)) + { + new = lsa; /* Don't touch this LSA. */ + goto out; + } + + if (IS_DEBUG_OSPF (lsa, LSA_INSTALL)) + zlog_info ("Install Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x]", lsa->data->type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr))); + + /* Replace the existing lsa with the new one. */ + if ((oipt = lookup_opaque_info_by_type (lsa)) != NULL + && (oipi = lookup_opaque_info_by_id (oipt, lsa)) != NULL) + { + ospf_lsa_unlock (oipi->lsa); + oipi->lsa = ospf_lsa_lock (lsa); + } + /* Register the new lsa entry and get its control info. */ + else + if ((oipi = register_opaque_lsa (lsa)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_install: register_opaque_lsa() ?"); + goto out; + } + + /* + * Make use of a common mechanism (ospf_lsa_refresh_walker) + * for periodic refresh of self-originated Opaque-LSAs. + */ + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + if (lsa->area == NULL || (top = lsa->area->top) == NULL) + { + /* Above conditions must have passed. */ + zlog_warn ("ospf_opaque_lsa_install: Sonmething wrong?"); + goto out; + } + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_top; + if (lsa->area != NULL && (top = lsa->area->top) == NULL) + { + /* Above conditions must have passed. */ + zlog_warn ("ospf_opaque_lsa_install: Sonmething wrong?"); + goto out; + } + break; + default: + zlog_warn ("ospf_opaque_lsa_install: Unexpected LSA-type(%u)", lsa->data->type); + goto out; + } + + ospf_refresher_register_lsa (top, lsa); + new = lsa; + +out: + return new; +} + +void +ospf_opaque_lsa_refresh (struct ospf_lsa *lsa) +{ + struct ospf_opaque_functab *functab; + + if ((functab = ospf_opaque_functab_lookup (lsa)) == NULL + || functab->lsa_refresher == NULL) + { + /* + * Though this LSA seems to have originated on this node, the + * handling module for this "lsa-type and opaque-type" was + * already deleted sometime ago. + * Anyway, this node still has a responsibility to flush this + * LSA from the routing domain. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("LSA[Type%d:%s]: Flush stray Opaque-LSA", lsa->data->type, inet_ntoa (lsa->data->id)); + + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + ospf_lsa_maxage (lsa); + } + else + (* functab->lsa_refresher)(lsa); + + return; +} + +/*------------------------------------------------------------------------* + * Followings are re-origination/refresh/flush operations of Opaque-LSAs, + * triggered by external interventions (vty session, signaling, etc). + *------------------------------------------------------------------------*/ + +#define OSPF_OPAQUE_TIMER_ON(T,F,L,V) \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), (L), (V)) + +static struct ospf_lsa *pseudo_lsa (struct ospf_interface *oi, struct ospf_area *area, u_char lsa_type, u_char opaque_type); +static int ospf_opaque_type9_lsa_reoriginate_timer (struct thread *t); +static int ospf_opaque_type10_lsa_reoriginate_timer (struct thread *t); +static int ospf_opaque_type11_lsa_reoriginate_timer (struct thread *t); +static int ospf_opaque_lsa_refresh_timer (struct thread *t); + +void +ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent, + u_char lsa_type, u_char opaque_type) +{ + struct ospf *top; + struct ospf_area dummy, *area = NULL; + struct ospf_interface *oi = NULL; + + struct ospf_lsa *lsa; + struct opaque_info_per_type *oipt; + int (* func)(struct thread *t) = NULL; + int delay; + + switch (lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + if ((oi = (struct ospf_interface *) lsa_type_dependent) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: Type-9 Opaque-LSA: Invalid parameter?"); + goto out; + } + if ((top = oi_to_top (oi)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: OI(%s) -> TOP?", IF_NAME (oi)); + goto out; + } + if (! list_isempty (ospf_opaque_type9_funclist) + && list_isempty (oi->opaque_lsa_self) + && oi->t_opaque_lsa_self != NULL) + { + zlog_warn ("Type-9 Opaque-LSA (opaque_type=%u): Common origination for OI(%s) has already started", opaque_type, IF_NAME (oi)); + goto out; + } + func = ospf_opaque_type9_lsa_reoriginate_timer; + break; + case OSPF_OPAQUE_AREA_LSA: + if ((area = (struct ospf_area *) lsa_type_dependent) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: Type-10 Opaque-LSA: Invalid parameter?"); + goto out; + } + if ((top = area->top) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: AREA(%s) -> TOP?", inet_ntoa (area->area_id)); + goto out; + } + if (! list_isempty (ospf_opaque_type10_funclist) + && list_isempty (area->opaque_lsa_self) + && area->t_opaque_lsa_self != NULL) + { + zlog_warn ("Type-10 Opaque-LSA (opaque_type=%u): Common origination for AREA(%s) has already started", opaque_type, inet_ntoa (area->area_id)); + goto out; + } + func = ospf_opaque_type10_lsa_reoriginate_timer; + break; + case OSPF_OPAQUE_AS_LSA: + if ((top = (struct ospf *) lsa_type_dependent) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: Type-11 Opaque-LSA: Invalid parameter?"); + goto out; + } + if (! list_isempty (ospf_opaque_type11_funclist) + && list_isempty (top->opaque_lsa_self) + && top->t_opaque_lsa_self != NULL) + { + zlog_warn ("Type-11 Opaque-LSA (opaque_type=%u): Common origination has already started", opaque_type); + goto out; + } + + /* Fake "area" to pass "ospf" to a lookup function later. */ + dummy.top = top; + area = &dummy; + + func = ospf_opaque_type11_lsa_reoriginate_timer; + break; + default: + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: Unexpected LSA-type(%u)", lsa_type); + goto out; + } + + /* It may not a right time to schedule reorigination now. */ + if (! CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("ospf_opaque_lsa_reoriginate_schedule: Not operational."); + goto out; /* This is not an error. */ + } + if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("ospf_opaque_lsa_reoriginate_schedule: Under blockade."); + goto out; /* This is not an error, too. */ + } + + /* Generate a dummy lsa to be passed for a lookup function. */ + lsa = pseudo_lsa (oi, area, lsa_type, opaque_type); + + if ((oipt = lookup_opaque_info_by_type (lsa)) == NULL) + { + struct ospf_opaque_functab *functab; + if ((functab = ospf_opaque_functab_lookup (lsa)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: No associated function?: lsa_type(%u), opaque_type(%u)", lsa_type, opaque_type); + goto out; + } + if ((oipt = register_opaque_info_per_type (functab, lsa)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: Cannot get a control info?: lsa_type(%u), opaque_type(%u)", lsa_type, opaque_type); + goto out; + } + } + + if (oipt->t_opaque_lsa_self != NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Type-%u Opaque-LSA has already scheduled to RE-ORIGINATE: [opaque-type=%u]", lsa_type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr))); + goto out; + } + + /* + * Different from initial origination time, in which various conditions + * (opaque capability, neighbor status etc) are assured by caller of + * the originating function "ospf_opaque_lsa_originate_schedule ()", + * it is highly possible that these conditions might not be satisfied + * at the time of re-origination function is to be called. + */ + delay = OSPF_MIN_LS_INTERVAL; /* XXX */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d sec later: [opaque-type=%u]", lsa_type, delay, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr))); + + OSPF_OPAQUE_TIMER_ON (oipt->t_opaque_lsa_self, func, oipt, delay); + +out: + return; +} + +static struct ospf_lsa * +pseudo_lsa (struct ospf_interface *oi, struct ospf_area *area, + u_char lsa_type, u_char opaque_type) +{ + static struct ospf_lsa lsa = { 0 }; + static struct lsa_header lsah = { 0 }; + u_int32_t tmp; + + lsa.oi = oi; + lsa.area = area; + lsa.data = &lsah; + + lsah.type = lsa_type; + tmp = SET_OPAQUE_LSID (opaque_type, 0); /* Opaque-ID is unused here. */ + lsah.id.s_addr = htonl (tmp); + + return &lsa; +} + +static int +ospf_opaque_type9_lsa_reoriginate_timer (struct thread *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + struct ospf *top; + struct ospf_interface *oi; + int rc = -1; + + oipt = THREAD_ARG (t); + oipt->t_opaque_lsa_self = NULL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + { + zlog_warn ("ospf_opaque_type9_lsa_reoriginate_timer: No associated function?"); + goto out; + } + + oi = (struct ospf_interface *) oipt->owner; + if ((top = oi_to_top (oi)) == NULL) + { + zlog_warn ("ospf_opaque_type9_lsa_reoriginate_timer: Something wrong?"); + goto out; + } + + if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE) + || ! ospf_if_is_enable (oi) + || ospf_opaque_capable_nbr_count (oi->nbrs, NSM_Full) == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + rc = 0; + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Timer[Type9-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for OI (%s)", oipt->opaque_type, IF_NAME (oi)); + + rc = (* functab->lsa_originator)(oi); +out: + return rc; +} + +static int +ospf_opaque_type10_lsa_reoriginate_timer (struct thread *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + listnode node; + struct ospf *top; + struct ospf_area *area; + struct ospf_interface *oi; + int n, rc = -1; + + oipt = THREAD_ARG (t); + oipt->t_opaque_lsa_self = NULL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + { + zlog_warn ("ospf_opaque_type10_lsa_reoriginate_timer: No associated function?"); + goto out; + } + + area = (struct ospf_area *) oipt->owner; + if (area == NULL || (top = area->top) == NULL) + { + zlog_warn ("ospf_opaque_type10_lsa_reoriginate_timer: Something wrong?"); + goto out; + } + + /* There must be at least one "opaque-capable, full-state" neighbor. */ + n = 0; + for (node = listhead (area->oiflist); node; nextnode (node)) + { + if ((oi = getdata (node)) == NULL) + continue; + if ((n = ospf_opaque_capable_nbr_count (oi->nbrs, NSM_Full)) > 0) + break; + } + + if (n == 0 || ! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Suspend re-origination of Type-10 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + rc = 0; + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Timer[Type10-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for Area %s", oipt->opaque_type, inet_ntoa (area->area_id)); + + rc = (* functab->lsa_originator)(area); +out: + return rc; +} + +static int +ospf_opaque_type11_lsa_reoriginate_timer (struct thread *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + struct ospf *top; + int rc = -1; + + oipt = THREAD_ARG (t); + oipt->t_opaque_lsa_self = NULL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + { + zlog_warn ("ospf_opaque_type11_lsa_reoriginate_timer: No associated function?"); + goto out; + } + + if ((top = (struct ospf *) oipt->owner) == NULL) + { + zlog_warn ("ospf_opaque_type11_lsa_reoriginate_timer: Something wrong?"); + goto out; + } + + if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Suspend re-origination of Type-11 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + rc = 0; + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Timer[Type11-LSA]: Re-originate Opaque-LSAs (opaque-type=%u).", oipt->opaque_type); + + rc = (* functab->lsa_originator)(top); +out: + return rc; +} + +extern int ospf_lsa_refresh_delay (struct ospf_lsa *); /* ospf_lsa.c */ + +void +ospf_opaque_lsa_refresh_schedule (struct ospf_lsa *lsa0) +{ + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi; + struct ospf_lsa *lsa; + int delay; + + if ((oipt = lookup_opaque_info_by_type (lsa0)) == NULL + || (oipi = lookup_opaque_info_by_id (oipt, lsa0)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_refresh_schedule: Invalid parameter?"); + goto out; + } + + /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */ + if ((lsa = oipi->lsa) == NULL) + { + zlog_warn ("ospf_opaque_lsa_refresh_schedule: Something wrong?"); + goto out; + } + + if (oipi->t_opaque_lsa_self != NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Type-%u Opaque-LSA has already scheduled to REFRESH: [opaque-type=%u, opaque-id=%x]", lsa->data->type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr))); + goto out; + } + + /* Delete this lsa from neighbor retransmit-list. */ + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_ls_retransmit_delete_nbr_all (lsa->area, lsa); + break; + case OSPF_OPAQUE_AS_LSA: + ospf_ls_retransmit_delete_nbr_all (NULL, lsa); + break; + default: + zlog_warn ("ospf_opaque_lsa_refresh_schedule: Unexpected LSA-type(%u)", lsa->data->type); + goto out; + } + + delay = ospf_lsa_refresh_delay (lsa); + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Schedule Type-%u Opaque-LSA to REFRESH in %d sec later: [opaque-type=%u, opaque-id=%x]", lsa->data->type, delay, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr))); + + OSPF_OPAQUE_TIMER_ON (oipi->t_opaque_lsa_self, + ospf_opaque_lsa_refresh_timer, oipi, delay); +out: + return; +} + +static int +ospf_opaque_lsa_refresh_timer (struct thread *t) +{ + struct opaque_info_per_id *oipi; + struct ospf_opaque_functab *functab; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Timer[Opaque-LSA]: (Opaque-LSA Refresh expire)"); + + oipi = THREAD_ARG (t); + oipi->t_opaque_lsa_self = NULL; + + if ((lsa = oipi->lsa) != NULL) + if ((functab = oipi->opqctl_type->functab) != NULL) + if (functab->lsa_refresher != NULL) + (* functab->lsa_refresher)(lsa); + + return 0; +} + +void +ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa0) +{ + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi; + struct ospf_lsa *lsa; + + if ((oipt = lookup_opaque_info_by_type (lsa0)) == NULL + || (oipi = lookup_opaque_info_by_id (oipt, lsa0)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_flush_schedule: Invalid parameter?"); + goto out; + } + + /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */ + if ((lsa = oipi->lsa) == NULL) + { + zlog_warn ("ospf_opaque_lsa_flush_schedule: Something wrong?"); + goto out; + } + + /* Delete this lsa from neighbor retransmit-list. */ + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_ls_retransmit_delete_nbr_all (lsa->area, lsa); + break; + case OSPF_OPAQUE_AS_LSA: + ospf_ls_retransmit_delete_nbr_all (NULL, lsa); + break; + default: + zlog_warn ("ospf_opaque_lsa_flush_schedule: Unexpected LSA-type(%u)", lsa->data->type); + goto out; + } + + /* Dequeue listnode entry from the list. */ + listnode_delete (oipt->id_list, oipi); + + /* Avoid misjudgement in the next lookup. */ + if (listcount (oipt->id_list) == 0) + oipt->id_list->head = oipt->id_list->tail = NULL; + + /* Disassociate internal control information with the given lsa. */ + oipi->lsa = NULL; + free_opaque_info_per_id ((void *) oipi); + + /* Force given lsa's age to MaxAge. */ + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Schedule Type-%u Opaque-LSA to FLUSH: [opaque-type=%u, opaque-id=%x]", lsa->data->type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr))); + + /* This lsa will be flushed and removed eventually. */ + ospf_lsa_maxage (lsa); + +out: + return; +} + +/*------------------------------------------------------------------------* + * Followings are control functions to block origination after restart. + *------------------------------------------------------------------------*/ + +static void ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs, struct ospf_neighbor *inbr, struct ospf_lsa *lsa); +static void ospf_opaque_type9_lsa_rxmt_nbr_check (struct ospf_interface *oi); +static void ospf_opaque_type10_lsa_rxmt_nbr_check (struct ospf_area *area); +static void ospf_opaque_type11_lsa_rxmt_nbr_check (struct ospf *top); +static unsigned long ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type); + +void +ospf_opaque_adjust_lsreq (struct ospf_neighbor *nbr, list lsas) +{ + struct ospf *top; + struct ospf_area *area; + struct ospf_interface *oi; + listnode node1, node2; + struct ospf_lsa *lsa; + + if ((top = oi_to_top (nbr->oi)) == NULL) + goto out; + + /* + * If an instance of self-originated Opaque-LSA is found in the given + * LSA list, and it is not installed to LSDB yet, exclude it from the + * list "nbr->ls_req". In this way, it is assured that an LSReq message, + * which might be sent in the process of flooding, will not request for + * the LSA to be flushed immediately; otherwise, depending on timing, + * an LSUpd message will carry instances of target LSAs with MaxAge, + * while other LSUpd message might carry old LSA instances (non-MaxAge). + * Obviously, the latter would trigger miserable situations that repeat + * installation and removal of unwanted LSAs indefinitely. + */ + for (node1 = listhead (lsas); node1; nextnode (node1)) + { + if ((lsa = getdata (node1)) == NULL) + continue; + + /* Filter out unwanted LSAs. */ + if (! IS_OPAQUE_LSA (lsa->data->type)) + continue; + if (! IPV4_ADDR_SAME (&lsa->data->adv_router, &top->router_id)) + continue; + + /* + * Don't touch an LSA which has MaxAge; two possible cases. + * + * 1) This LSA has originally flushed by myself (received LSUpd + * message's router-id is equal to my router-id), and flooded + * back by an opaque-capable router. + * + * 2) This LSA has expired in an opaque-capable router and thus + * flushed by the router. + */ + if (IS_LSA_MAXAGE (lsa)) + continue; + + /* If the LSA has installed in the LSDB, nothing to do here. */ + if (ospf_lsa_lookup_by_header (nbr->oi->area, lsa->data) != NULL) + continue; + + /* Ok, here we go. */ + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + oi = nbr->oi; + ospf_opaque_exclude_lsa_from_lsreq (oi->nbrs, nbr, lsa); + break; + case OSPF_OPAQUE_AREA_LSA: + area = nbr->oi->area; + for (node2 = listhead (area->oiflist); node2; nextnode (node2)) + { + if ((oi = getdata (node2)) == NULL) + continue; + ospf_opaque_exclude_lsa_from_lsreq (oi->nbrs, nbr, lsa); + } + break; + case OSPF_OPAQUE_AS_LSA: + for (node2 = listhead (top->oiflist); node2; nextnode (node2)) + { + if ((oi = getdata (node2)) == NULL) + continue; + ospf_opaque_exclude_lsa_from_lsreq (oi->nbrs, nbr, lsa); + } + break; + default: + break; + } + } + +out: + return; +} + +static void +ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs, + struct ospf_neighbor *inbr, + struct ospf_lsa *lsa) +{ + struct route_node *rn; + struct ospf_neighbor *onbr; + struct ospf_lsa *ls_req; + + for (rn = route_top (nbrs); rn; rn = route_next (rn)) + { + if ((onbr = rn->info) == NULL) + continue; + if (onbr == inbr) + continue; + if ((ls_req = ospf_ls_request_lookup (onbr, lsa)) == NULL) + continue; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("LSA[%s]: Exclude this entry from LSReq to send.", dump_lsa_key (lsa)); + + ospf_ls_request_delete (onbr, ls_req); +/* ospf_check_nbr_loading (onbr);*//* XXX */ + } + + return; +} + +void +ospf_opaque_self_originated_lsa_received (struct ospf_neighbor *nbr, list lsas) +{ + struct ospf *top; + listnode node, next; + struct ospf_lsa *lsa; + u_char before; + + if ((top = oi_to_top (nbr->oi)) == NULL) + goto out; + + before = IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque); + + for (node = listhead (lsas); node; node = next) + { + next = node->next; + + if ((lsa = getdata (node)) == NULL) + continue; + + listnode_delete (lsas, lsa); + + /* + * Since these LSA entries are not yet installed into corresponding + * LSDB, just flush them without calling ospf_ls_maxage() afterward. + */ + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + SET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT); + ospf_flood_through_area (nbr->oi->area, NULL/*inbr*/, lsa); + break; + case OSPF_OPAQUE_AREA_LSA: + SET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT); + ospf_flood_through_area (nbr->oi->area, NULL/*inbr*/, lsa); + break; + case OSPF_OPAQUE_AS_LSA: + SET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT); + ospf_flood_through_as (NULL/*inbr*/, lsa); + break; + default: + zlog_warn ("ospf_opaque_self_originated_lsa_received: Unexpected LSA-type(%u)", lsa->data->type); + goto out; + } + + ospf_lsa_discard (lsa); /* List "lsas" will be deleted by caller. */ + } + + if (before == 0 && IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Block Opaque-LSA origination: OFF -> ON"); + } + +out: + return; +} + +void +ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, list acks) +{ + struct ospf *top; + listnode node; + struct ospf_lsa *lsa; + char type9_lsa_rcv = 0, type10_lsa_rcv = 0, type11_lsa_rcv = 0; + + if ((top = oi_to_top (nbr->oi)) == NULL) + goto out; + + for (node = listhead (acks); node; nextnode (node)) + { + if ((lsa = getdata (node)) == NULL) + continue; + + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + type9_lsa_rcv = 1; + /* Callback function... */ + break; + case OSPF_OPAQUE_AREA_LSA: + type10_lsa_rcv = 1; + /* Callback function... */ + break; + case OSPF_OPAQUE_AS_LSA: + type11_lsa_rcv = 1; + /* Callback function... */ + break; + default: + zlog_warn ("ospf_opaque_ls_ack_received: Unexpected LSA-type(%u)", lsa->data->type); + goto out; + } + } + + if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) + { + int delay; + struct ospf_interface *oi; + + if (type9_lsa_rcv + && CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT)) + ospf_opaque_type9_lsa_rxmt_nbr_check (nbr->oi); + + if (type10_lsa_rcv + && CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT)) + ospf_opaque_type10_lsa_rxmt_nbr_check (nbr->oi->area); + + if (type11_lsa_rcv + && CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT)) + ospf_opaque_type11_lsa_rxmt_nbr_check (top); + + if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) + goto out; /* Blocking still in progress. */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Block Opaque-LSA origination: ON -> OFF"); + + if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)) + goto out; /* Opaque capability condition must have changed. */ + + /* Ok, let's start origination of Opaque-LSAs. */ + delay = OSPF_MIN_LS_INTERVAL; + for (node = listhead (top->oiflist); node; nextnode (node)) + { + if ((oi = getdata (node)) == NULL) + continue; + + if (! ospf_if_is_enable (oi) + || ospf_opaque_capable_nbr_count (oi->nbrs, NSM_Full) == 0) + continue; + + ospf_opaque_lsa_originate_schedule (oi, &delay); + } + } + +out: + return; +} + +static void +ospf_opaque_type9_lsa_rxmt_nbr_check (struct ospf_interface *oi) +{ + unsigned long n; + + n = ospf_opaque_nrxmt_self (oi->nbrs, OSPF_OPAQUE_LINK_LSA); + if (n == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Self-originated type-9 Opaque-LSAs: OI(%s): Flush completed", IF_NAME (oi)); + + UNSET_FLAG (oi->area->top->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT); + } + return; +} + +static void +ospf_opaque_type10_lsa_rxmt_nbr_check (struct ospf_area *area) +{ + listnode node; + struct ospf_interface *oi; + unsigned long n = 0; + + for (node = listhead (area->oiflist); node; nextnode (node)) + { + if ((oi = getdata (node)) == NULL) + continue; + + if (area->area_id.s_addr != OSPF_AREA_BACKBONE + && oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + n = ospf_opaque_nrxmt_self (oi->nbrs, OSPF_OPAQUE_AREA_LSA); + if (n > 0) + break; + } + + if (n == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Self-originated type-10 Opaque-LSAs: AREA(%s): Flush completed", inet_ntoa (area->area_id)); + + UNSET_FLAG (area->top->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT); + } + + return; +} + +static void +ospf_opaque_type11_lsa_rxmt_nbr_check (struct ospf *top) +{ + listnode node; + struct ospf_interface *oi; + unsigned long n = 0; + + for (node = listhead (top->oiflist); node; nextnode (node)) + { + if ((oi = getdata (node)) == NULL) + continue; + + switch (oi->type) + { + case OSPF_IFTYPE_VIRTUALLINK: + continue; + default: + break; + } + + n = ospf_opaque_nrxmt_self (oi->nbrs, OSPF_OPAQUE_AS_LSA); + if (n > 0) + goto out; + } + + if (n == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Self-originated type-11 Opaque-LSAs: Flush completed"); + + UNSET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT); + } + +out: + return; +} + +static unsigned long +ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + struct ospf *top; + unsigned long n = 0; + + for (rn = route_top (nbrs); rn; rn = route_next (rn)) + { + if ((nbr = rn->info) == NULL) + continue; + if ((top = oi_to_top (nbr->oi)) == NULL) + continue; + if (IPV4_ADDR_SAME (&nbr->router_id, &top->router_id)) + continue; + n += ospf_ls_retransmit_count_self (nbr, lsa_type); + } + + return n; +} + +/*------------------------------------------------------------------------* + * Followings are util functions; probably be used by Opaque-LSAs only... + *------------------------------------------------------------------------*/ + +void +htonf (float *src, float *dst) +{ + u_int32_t lu1, lu2; + + memcpy (&lu1, src, sizeof (u_int32_t)); + lu2 = htonl (lu1); + memcpy (dst, &lu2, sizeof (u_int32_t)); + return; +} + +void +ntohf (float *src, float *dst) +{ + u_int32_t lu1, lu2; + + memcpy (&lu1, src, sizeof (u_int32_t)); + lu2 = ntohl (lu1); + memcpy (dst, &lu2, sizeof (u_int32_t)); + return; +} + +struct ospf * +oi_to_top (struct ospf_interface *oi) +{ + struct ospf *top = NULL; + struct ospf_area *area; + + if (oi == NULL || (area = oi->area) == NULL || (top = area->top) == NULL) + zlog_warn ("Broken relationship for \"OI -> AREA -> OSPF\"?"); + + return top; +} + +#endif /* HAVE_OPAQUE_LSA */ |