// SPDX-License-Identifier: GPL-2.0-or-later /* * This is an implementation of rfc2370. * Copyright (C) 2001 KDD R&D Laboratories, Inc. * http://www.kddlabs.co.jp/ */ #include #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 "frrevent.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "printfrr.h" #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" #include "ospfd/ospf_te.h" #include "ospfd/ospf_sr.h" #include "ospfd/ospf_ri.h" #include "ospfd/ospf_ext.h" #include "ospfd/ospf_errors.h" DEFINE_MTYPE_STATIC(OSPFD, OSPF_OPAQUE_FUNCTAB, "OSPF opaque function table"); DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_TYPE, "OSPF opaque per-type info"); DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_ID, "OSPF opaque per-ID info"); /*------------------------------------------------------------------------* * Following are initialize/terminate functions for Opaque-LSAs handling. *------------------------------------------------------------------------*/ #ifdef SUPPORT_OSPF_API int ospf_apiserver_init(void); void ospf_apiserver_term(void); /* Init apiserver? It's disabled by default. */ int ospf_apiserver_enable; #endif /* SUPPORT_OSPF_API */ 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_del(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(); if (ospf_mpls_te_init() != 0) exit(1); /* Segment Routing init */ if (ospf_sr_init() != 0) exit(1); if (ospf_router_info_init() != 0) exit(1); if (ospf_ext_init() != 0) exit(1); #ifdef SUPPORT_OSPF_API if ((ospf_apiserver_enable) && (ospf_apiserver_init() != 0)) exit(1); #endif /* SUPPORT_OSPF_API */ return; } void ospf_opaque_term(void) { ospf_mpls_te_term(); ospf_router_info_term(); ospf_ext_term(); ospf_sr_term(); #ifdef SUPPORT_OSPF_API ospf_apiserver_term(); #endif /* SUPPORT_OSPF_API */ ospf_opaque_funclist_term(); return; } void ospf_opaque_finish(void) { ospf_mpls_te_finish(); ospf_router_info_finish(); ospf_ext_finish(); #ifdef SUPPORT_OSPF_API ospf_apiserver_term(); #endif ospf_sr_finish(); } 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_del; oi->t_opaque_lsa_self = NULL; return 0; } void ospf_opaque_type9_lsa_term(struct ospf_interface *oi) { EVENT_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_del; 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 */ EVENT_OFF(area->t_opaque_lsa_self); if (area->opaque_lsa_self != NULL) list_delete(&area->opaque_lsa_self); 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_del; 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 */ EVENT_OFF(top->t_opaque_lsa_self); if (top->opaque_lsa_self != NULL) list_delete(&top->opaque_lsa_self); return; } static const char *ospf_opaque_type_name(uint8_t 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; case OPAQUE_TYPE_INTER_AS_LSA: name = "Inter-AS TE-v2 LSA"; break; case OPAQUE_TYPE_ROUTER_INFORMATION_LSA: name = "Router Information LSA"; break; case OPAQUE_TYPE_EXTENDED_PREFIX_LSA: name = "Extended Prefix Opaque LSA"; break; case OPAQUE_TYPE_EXTENDED_LINK_LSA: name = "Extended Link Opaque LSA"; break; default: if (OPAQUE_TYPE_RANGE_UNASSIGNED(opaque_type)) name = "Unassigned"; else { uint32_t bigger_range = opaque_type; /* * Get around type-limits warning: comparison is always * true due to limited range of data type */ if (OPAQUE_TYPE_RANGE_RESERVED(bigger_range)) name = "Private/Experimental"; } break; } return name; } /*------------------------------------------------------------------------* * Following are management functions to store user specified callbacks. *------------------------------------------------------------------------*/ struct opaque_info_per_type; /* Forward declaration. */ static void free_opaque_info_per_type(struct opaque_info_per_type *oipt, bool cleanup_owner); struct ospf_opaque_functab { uint8_t 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 json_object *json, struct ospf_lsa *lsa); int (*lsa_originator)(void *arg); struct ospf_lsa *(*lsa_refresher)(struct ospf_lsa *lsa); int (*new_lsa_hook)(struct ospf_lsa *lsa); int (*del_lsa_hook)(struct ospf_lsa *lsa); }; /* Handle LSA-9/10/11 altogether. */ static struct list *ospf_opaque_wildcard_funclist; static struct list *ospf_opaque_type9_funclist; static struct list *ospf_opaque_type10_funclist; static struct 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) { struct list *funclist; funclist = ospf_opaque_wildcard_funclist = list_new(); funclist->del = ospf_opaque_del_functab; 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) { struct list *funclist; funclist = ospf_opaque_wildcard_funclist; list_delete(&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 struct list *ospf_get_opaque_funclist(uint8_t lsa_type) { struct list *funclist = NULL; switch (lsa_type) { case OPAQUE_TYPE_WILDCARD: /* XXX * This is an ugly trick to handle type-9/10/11 LSA altogether. * Yes, "OPAQUE_TYPE_WILDCARD (value 0)" is not an LSA-type, nor * an officially assigned opaque-type. * Though it is possible that the value might be officially used * in the future, we use it internally as a special label, for * now. */ funclist = ospf_opaque_wildcard_funclist; break; 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: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", __func__, lsa_type); break; } return funclist; } /* XXX: such a huge argument list can /not/ be healthy... */ int ospf_register_opaque_functab( uint8_t lsa_type, uint8_t 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 json_object *json, struct ospf_lsa *lsa), int (*lsa_originator)(void *arg), struct ospf_lsa *(*lsa_refresher)(struct ospf_lsa *lsa), int (*new_lsa_hook)(struct ospf_lsa *lsa), int (*del_lsa_hook)(struct ospf_lsa *lsa)) { struct list *funclist; struct ospf_opaque_functab *new; if ((funclist = ospf_get_opaque_funclist(lsa_type)) == NULL) return -1; struct listnode *node, *nnode; struct ospf_opaque_functab *functab; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) if (functab->opaque_type == opaque_type) { flog_warn( EC_OSPF_LSA, "%s: Duplicated entry?: lsa_type(%u), opaque_type(%u)", __func__, lsa_type, opaque_type); return -1; } new = XCALLOC(MTYPE_OSPF_OPAQUE_FUNCTAB, sizeof(struct ospf_opaque_functab)); 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); return 0; } void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type) { struct list *funclist; struct listnode *node, *nnode; struct ospf_opaque_functab *functab; if ((funclist = ospf_get_opaque_funclist(lsa_type)) != NULL) for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) { if (functab->opaque_type == opaque_type) { /* Cleanup internal control information, if it * still remains. */ if (functab->oipt != NULL) free_opaque_info_per_type(functab->oipt, true); /* Dequeue listnode entry from the list. */ listnode_delete(funclist, functab); XFREE(MTYPE_OSPF_OPAQUE_FUNCTAB, functab); break; } } return; } static struct ospf_opaque_functab * ospf_opaque_functab_lookup(struct ospf_lsa *lsa) { struct list *funclist; struct listnode *node; struct ospf_opaque_functab *functab; uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); if ((funclist = ospf_get_opaque_funclist(lsa->data->type)) != NULL) for (ALL_LIST_ELEMENTS_RO(funclist, node, functab)) if (functab->opaque_type == key) return functab; return NULL; } /*------------------------------------------------------------------------* * Following 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 { uint8_t lsa_type; uint8_t 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 event *t_opaque_lsa_self; /* * Backpointer to an "owner" which is LSA-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 information per opaque-id. */ struct list *id_list; }; /* Opaque-LSA control information per opaque-id. */ struct opaque_info_per_id { uint32_t opaque_id; /* Thread for refresh/flush scheduling for this opaque-type/id. */ struct event *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; oipt = XCALLOC(MTYPE_OPAQUE_INFO_PER_TYPE, sizeof(struct opaque_info_per_type)); 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_lookup_by_vrf_id(VRF_DEFAULT); if (new->area != NULL && (top = new->area->ospf) == NULL) { free_opaque_info_per_type(oipt, true); oipt = NULL; goto out; /* This case may not exist. */ } oipt->owner = top; listnode_add(top->opaque_lsa_self, oipt); break; default: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", __func__, new->data->type); free_opaque_info_per_type(oipt, true); oipt = NULL; goto out; /* This case may not exist. */ } oipt->lsa_type = new->data->type; oipt->opaque_type = GET_OPAQUE_TYPE(ntohl(new->data->id.s_addr)); oipt->status = PROC_NORMAL; 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(struct opaque_info_per_type *oipt, bool cleanup_owner) { struct opaque_info_per_id *oipi; struct ospf_lsa *lsa; struct listnode *node, *nnode; struct list *l; /* Control information per opaque-id may still exist. */ for (ALL_LIST_ELEMENTS(oipt->id_list, node, nnode, oipi)) { if ((lsa = oipi->lsa) == NULL) continue; if (IS_LSA_MAXAGE(lsa)) continue; ospf_opaque_lsa_flush_schedule(lsa); } EVENT_OFF(oipt->t_opaque_lsa_self); list_delete(&oipt->id_list); if (cleanup_owner) { /* Remove from its owner's self-originated LSA list. */ switch (oipt->lsa_type) { case OSPF_OPAQUE_LINK_LSA: l = ((struct ospf_interface *)oipt->owner) ->opaque_lsa_self; break; case OSPF_OPAQUE_AREA_LSA: l = ((struct ospf_area *)oipt->owner)->opaque_lsa_self; break; case OSPF_OPAQUE_AS_LSA: l = ((struct ospf *)oipt->owner)->opaque_lsa_self; break; default: flog_warn( EC_OSPF_LSA_UNEXPECTED, "free_opaque_info_owner: Unexpected LSA-type(%u)", oipt->lsa_type); return; } listnode_delete(l, oipt); } XFREE(MTYPE_OPAQUE_INFO_PER_TYPE, oipt); return; } static void free_opaque_info_per_type_del(void *val) { free_opaque_info_per_type((struct opaque_info_per_type *)val, false); } 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; struct list *listtop = NULL; struct listnode *node, *nnode; struct opaque_info_per_type *oipt = NULL; uint8_t 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 flog_warn( EC_OSPF_LSA, "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 flog_warn( EC_OSPF_LSA, "Type-10 Opaque-LSA: Reference to AREA is missing?"); break; case OSPF_OPAQUE_AS_LSA: top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((area = lsa->area) != NULL && (top = area->ospf) == NULL) { flog_warn( EC_OSPF_LSA, "Type-11 Opaque-LSA: Reference to OSPF is missing?"); break; /* Unlikely to happen. */ } listtop = top->opaque_lsa_self; break; default: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", __func__, lsa->data->type); break; } if (listtop != NULL) for (ALL_LIST_ELEMENTS(listtop, node, nnode, oipt)) 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; oipi = XCALLOC(MTYPE_OPAQUE_INFO_PER_ID, sizeof(struct opaque_info_per_id)); oipi->opaque_id = GET_OPAQUE_ID(ntohl(new->data->id.s_addr)); oipi->opqctl_type = oipt; oipi->lsa = ospf_lsa_lock(new); listnode_add(oipt->id_list, oipi); return oipi; } static void free_opaque_info_per_id(void *val) { struct opaque_info_per_id *oipi = (struct opaque_info_per_id *)val; EVENT_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) { struct listnode *node, *nnode; struct opaque_info_per_id *oipi; uint32_t key = GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)); for (ALL_LIST_ELEMENTS(oipt->id_list, node, nnode, oipi)) 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; } int ospf_opaque_is_owned(struct ospf_lsa *lsa) { struct opaque_info_per_type *oipt = lookup_opaque_info_by_type(lsa); return (oipt != NULL && lookup_opaque_info_by_id(oipt, lsa) != NULL); } /*------------------------------------------------------------------------* * Following are (vty) configuration functions for Opaque-LSAs handling. *------------------------------------------------------------------------*/ DEFUN (capability_opaque, capability_opaque_cmd, "capability opaque", "Enable specific OSPF feature\n" "Opaque LSA\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); /* Check that OSPF is using default VRF */ if (ospf->vrf_id != VRF_DEFAULT) { vty_out(vty, "OSPF Opaque LSA is only supported in default VRF\n"); return CMD_WARNING_CONFIG_FAILED; } /* Turn on the "master switch" of opaque-lsa capability. */ if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("Opaque capability: OFF -> ON"); SET_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE); ospf_renegotiate_optional_capabilities(ospf); } return CMD_SUCCESS; } DEFUN (ospf_opaque, ospf_opaque_cmd, "ospf opaque-lsa", "OSPF specific commands\n" "Enable the Opaque-LSA capability (rfc2370)\n") { return capability_opaque(self, vty, argc, argv); } DEFUN (no_capability_opaque, no_capability_opaque_cmd, "no capability opaque", NO_STR "Enable specific OSPF feature\n" "Opaque LSA\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); /* Turn off the "master switch" of opaque-lsa capability. */ if (CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("Opaque capability: ON -> OFF"); UNSET_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE); ospf_renegotiate_optional_capabilities(ospf); } return CMD_SUCCESS; } DEFUN (no_ospf_opaque, no_ospf_opaque_cmd, "no ospf opaque-lsa", NO_STR "OSPF specific commands\n" "Enable the Opaque-LSA capability (rfc2370)\n") { return no_capability_opaque(self, vty, argc, argv); } 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_cmd); install_element(OSPF_NODE, &no_ospf_opaque_cmd); return; } /*------------------------------------------------------------------------* * Following are collection of user-registered function callers. *------------------------------------------------------------------------*/ static int opaque_lsa_new_if_callback(struct list *funclist, struct interface *ifp) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; int rc = -1; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) 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(struct list *funclist, struct interface *ifp) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; int rc = -1; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) 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(struct list *funclist, struct ospf_interface *oi, int old_status) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) if (functab->ism_change_hook != NULL) (*functab->ism_change_hook)(oi, old_status); return; } static void opaque_lsa_nsm_change_callback(struct list *funclist, struct ospf_neighbor *nbr, int old_status) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) if (functab->nsm_change_hook != NULL) (*functab->nsm_change_hook)(nbr, old_status); return; } static void opaque_lsa_config_write_router_callback(struct list *funclist, struct vty *vty) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) if (functab->config_write_router != NULL) (*functab->config_write_router)(vty); return; } static void opaque_lsa_config_write_if_callback(struct list *funclist, struct vty *vty, struct interface *ifp) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) if (functab->config_write_if != NULL) (*functab->config_write_if)(vty, ifp); return; } static void opaque_lsa_config_write_debug_callback(struct list *funclist, struct vty *vty) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) if (functab->config_write_debug != NULL) (*functab->config_write_debug)(vty); return; } static int opaque_lsa_originate_callback(struct list *funclist, void *lsa_type_dependent) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; int rc = -1; for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) 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(struct list *funclist, struct ospf_lsa *lsa) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; int rc = -1; /* This function handles ALL types of LSAs, not only opaque ones. */ for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) 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(struct list *funclist, struct ospf_lsa *lsa) { struct listnode *node, *nnode; struct ospf_opaque_functab *functab; int rc = -1; /* This function handles ALL types of LSAs, not only opaque ones. */ for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) if (functab->del_lsa_hook != NULL) if ((*functab->del_lsa_hook)(lsa) != 0) goto out; rc = 0; out: return rc; } /*------------------------------------------------------------------------* * Following are glue functions to call Opaque-LSA specific processing. *------------------------------------------------------------------------*/ int ospf_opaque_new_if(struct interface *ifp) { struct list *funclist; int rc = -1; funclist = ospf_opaque_wildcard_funclist; if (opaque_lsa_new_if_callback(funclist, ifp) != 0) goto out; 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) { struct list *funclist; int rc = -1; funclist = ospf_opaque_wildcard_funclist; if (opaque_lsa_del_if_callback(funclist, ifp) != 0) goto out; 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) { struct list *funclist; funclist = ospf_opaque_wildcard_funclist; opaque_lsa_ism_change_callback(funclist, oi, old_status); 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; struct 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_debug( "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_wildcard_funclist; opaque_lsa_nsm_change_callback(funclist, nbr, old_state); 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) { struct list *funclist; if (CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) vty_out(vty, " capability opaque\n"); funclist = ospf_opaque_wildcard_funclist; opaque_lsa_config_write_router_callback(funclist, vty); 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) { struct list *funclist; funclist = ospf_opaque_wildcard_funclist; opaque_lsa_config_write_if_callback(funclist, vty, ifp); 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) { struct list *funclist; funclist = ospf_opaque_wildcard_funclist; opaque_lsa_config_write_debug_callback(funclist, vty); 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, json_object *json) { char buf[128], *bp; struct lsa_header *lsah = lsa->data; uint32_t lsid = ntohl(lsah->id.s_addr); uint8_t opaque_type = GET_OPAQUE_TYPE(lsid); uint32_t opaque_id = GET_OPAQUE_ID(lsid); struct ospf_opaque_functab *functab; int len, lenValid; /* Switch output functionality by vty address. */ if (vty != NULL) { if (!json) { vty_out(vty, " Opaque-Type %u (%s)\n", opaque_type, ospf_opaque_type_name(opaque_type)); vty_out(vty, " Opaque-ID 0x%x\n", opaque_id); vty_out(vty, " Opaque-Info: %u octets of data%s\n", ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE, VALID_OPAQUE_INFO_LEN(lsah) ? "" : "(Invalid length?)"); } else { json_object_string_add( json, "opaqueType", ospf_opaque_type_name(opaque_type)); json_object_int_add(json, "opaqueId", opaque_id); len = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE; json_object_int_add(json, "opaqueDataLength", len); lenValid = VALID_OPAQUE_INFO_LEN(lsah); json_object_boolean_add(json, "opaqueDataLengthValid", lenValid); if (lenValid) { bp = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), "%*pHXn", (int)len, (lsah + 1)); json_object_string_add(json, "opaqueData", buf); if (bp != buf) XFREE(MTYPE_TMP, bp); } } } else { zlog_debug(" Opaque-Type %u (%s)", opaque_type, ospf_opaque_type_name(opaque_type)); zlog_debug(" Opaque-ID 0x%x", opaque_id); zlog_debug(" 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, json, lsa); return; } void ospf_opaque_lsa_dump(struct stream *s, uint16_t length) { struct ospf_lsa lsa = {}; lsa.data = (struct lsa_header *)stream_pnt(s); lsa.size = length; show_opaque_info_detail(NULL, &lsa, NULL); return; } static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa) { struct 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_wildcard_funclist; if (new_lsa_callback(funclist, lsa) != 0) goto out; 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) { struct 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_wildcard_funclist; if (del_lsa_callback(funclist, lsa) != 0) goto out; 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; } /*------------------------------------------------------------------------* * Following are Opaque-LSA origination/refresh management functions. *------------------------------------------------------------------------*/ static void ospf_opaque_type9_lsa_originate(struct event *t); static void ospf_opaque_type10_lsa_originate(struct event *t); static void ospf_opaque_type11_lsa_originate(struct event *t); static void ospf_opaque_lsa_reoriginate_resume(struct list *listtop, void *arg); void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0) { struct ospf *top; struct ospf_area *area; struct listnode *node, *nnode; struct opaque_info_per_type *oipt; int delay = 0; if ((top = oi_to_top(oi)) == NULL || (area = oi->area) == NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: Invalid argument?", __func__); return; } /* 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_debug("%s: Not operational.", __func__); return; /* This is not an error. */ } 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_debug( "Schedule Type-9 Opaque-LSA origination in %d ms later.", delay); oi->t_opaque_lsa_self = NULL; event_add_timer_msec(master, ospf_opaque_type9_lsa_originate, oi, delay, &oi->t_opaque_lsa_self); delay += top->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_debug( "Schedule Type-10 Opaque-LSA origination in %d ms later.", delay); area->t_opaque_lsa_self = NULL; event_add_timer_msec(master, ospf_opaque_type10_lsa_originate, area, delay, &area->t_opaque_lsa_self); delay += top->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_debug( "Schedule Type-11 Opaque-LSA origination in %d ms later.", delay); top->t_opaque_lsa_self = NULL; event_add_timer_msec(master, ospf_opaque_type11_lsa_originate, top, delay, &top->t_opaque_lsa_self); delay += top->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 (ALL_LIST_ELEMENTS(oi->opaque_lsa_self, node, nnode, oipt)) { /* * removed the test for * (! list_isempty (oipt->id_list)) * Handler is * already active. * * because opaque cababilities ON -> OFF -> ON result in * list_isempty (oipt->id_list) * not being empty. */ if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ || oipt->status == PROC_SUSPEND) /* Cannot originate now. */ 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 (ALL_LIST_ELEMENTS(area->opaque_lsa_self, node, nnode, oipt)) { /* * removed the test for * (! list_isempty (oipt->id_list)) * Handler is * already active. * * because opaque cababilities ON -> OFF -> ON result in * list_isempty (oipt->id_list) * not being empty. */ if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ || oipt->status == PROC_SUSPEND) /* Cannot originate now. */ 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 (ALL_LIST_ELEMENTS(top->opaque_lsa_self, node, nnode, oipt)) { /* * removed the test for * (! list_isempty (oipt->id_list)) * Handler is * already active. * * because opaque cababilities ON -> OFF -> ON result in * list_isempty (oipt->id_list) * not being empty. */ if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ || oipt->status == PROC_SUSPEND) /* Cannot originate now. */ continue; ospf_opaque_lsa_reoriginate_schedule((void *)top, OSPF_OPAQUE_AS_LSA, oipt->opaque_type); } } if (delay0 != NULL) *delay0 = delay; } static void ospf_opaque_type9_lsa_originate(struct event *t) { struct ospf_interface *oi; oi = EVENT_ARG(t); oi->t_opaque_lsa_self = NULL; if (IS_DEBUG_OSPF_EVENT) zlog_debug("Timer[Type9-LSA]: Originate Opaque-LSAs for OI %s", IF_NAME(oi)); opaque_lsa_originate_callback(ospf_opaque_type9_funclist, oi); } static void ospf_opaque_type10_lsa_originate(struct event *t) { struct ospf_area *area; area = EVENT_ARG(t); area->t_opaque_lsa_self = NULL; if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Timer[Type10-LSA]: Originate Opaque-LSAs for Area %pI4", &area->area_id); opaque_lsa_originate_callback(ospf_opaque_type10_funclist, area); } static void ospf_opaque_type11_lsa_originate(struct event *t) { struct ospf *top; top = EVENT_ARG(t); top->t_opaque_lsa_self = NULL; if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Timer[Type11-LSA]: Originate AS-External Opaque-LSAs"); opaque_lsa_originate_callback(ospf_opaque_type11_funclist, top); } static void ospf_opaque_lsa_reoriginate_resume(struct list *listtop, void *arg) { struct listnode *node, *nnode; 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 (ALL_LIST_ELEMENTS(listtop, node, nnode, oipt)) { if (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) { flog_warn(EC_OSPF_LSA, "%s: Failed (opaque-type=%u)", __func__, 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_debug( "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 */ else if (register_opaque_lsa(lsa) == NULL) { flog_warn(EC_OSPF_LSA, "%s: register_opaque_lsa() ?", __func__); 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: if ((top = oi_to_top(lsa->oi)) == NULL) { /* Above conditions must have passed. */ flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); goto out; } break; case OSPF_OPAQUE_AREA_LSA: if (lsa->area == NULL || (top = lsa->area->ospf) == NULL) { /* Above conditions must have passed. */ flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); goto out; } break; case OSPF_OPAQUE_AS_LSA: top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (lsa->area != NULL && (top = lsa->area->ospf) == NULL) { /* Above conditions must have passed. */ flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); goto out; } break; default: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", __func__, lsa->data->type); goto out; } ospf_refresher_register_lsa(top, lsa); new = lsa; out: return new; } struct ospf_lsa *ospf_opaque_lsa_refresh(struct ospf_lsa *lsa) { struct ospf *ospf; struct ospf_opaque_functab *functab; struct ospf_lsa *new = NULL; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); 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_debug("LSA[Type%d:%pI4]: Flush stray Opaque-LSA", lsa->data->type, &lsa->data->id); lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); ospf_lsa_flush(ospf, lsa); } else new = (*functab->lsa_refresher)(lsa); return new; } /*------------------------------------------------------------------------* * Following 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) \ event_add_timer_msec(master, (F), (L), (V), &(T)) static struct ospf_lsa *pseudo_lsa(struct ospf_interface *oi, struct ospf_area *area, uint8_t lsa_type, uint8_t opaque_type); static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t); static void ospf_opaque_type10_lsa_reoriginate_timer(struct event *t); static void ospf_opaque_type11_lsa_reoriginate_timer(struct event *t); static void ospf_opaque_lsa_refresh_timer(struct event *t); void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, uint8_t lsa_type, uint8_t opaque_type) { struct ospf *top = NULL; struct ospf_area dummy, *area = NULL; struct ospf_interface *oi = NULL; struct ospf_lsa *lsa; struct opaque_info_per_type *oipt; void (*func)(struct event * t) = NULL; int delay; switch (lsa_type) { case OSPF_OPAQUE_LINK_LSA: if ((oi = (struct ospf_interface *)lsa_type_dependent) == NULL) { flog_warn(EC_OSPF_LSA, "%s: Type-9 Opaque-LSA: Invalid parameter?", __func__); goto out; } if ((top = oi_to_top(oi)) == NULL) { flog_warn(EC_OSPF_LSA, "%s: OI(%s) -> TOP?", __func__, IF_NAME(oi)); goto out; } if (!list_isempty(ospf_opaque_type9_funclist) && list_isempty(oi->opaque_lsa_self) && oi->t_opaque_lsa_self != NULL) { flog_warn( EC_OSPF_LSA, "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) { flog_warn(EC_OSPF_LSA, "%s: Type-10 Opaque-LSA: Invalid parameter?", __func__); goto out; } if ((top = area->ospf) == NULL) { flog_warn(EC_OSPF_LSA, "%s: AREA(%pI4) -> TOP?", __func__, &area->area_id); goto out; } if (!list_isempty(ospf_opaque_type10_funclist) && list_isempty(area->opaque_lsa_self) && area->t_opaque_lsa_self != NULL) { flog_warn( EC_OSPF_LSA, "Type-10 Opaque-LSA (opaque_type=%u): Common origination for AREA(%pI4) has already started", opaque_type, &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) { flog_warn(EC_OSPF_LSA, "%s: Type-11 Opaque-LSA: Invalid parameter?", __func__); goto out; } if (!list_isempty(ospf_opaque_type11_funclist) && list_isempty(top->opaque_lsa_self) && top->t_opaque_lsa_self != NULL) { flog_warn( EC_OSPF_LSA, "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.ospf = top; area = &dummy; func = ospf_opaque_type11_lsa_reoriginate_timer; break; default: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", __func__, 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_debug("%s: Not operational.", __func__); goto out; /* This is not an error. */ } /* Generate a dummy lsa to be passed for a lookup function. */ lsa = pseudo_lsa(oi, area, lsa_type, opaque_type); lsa->vrf_id = VRF_DEFAULT; if ((oipt = lookup_opaque_info_by_type(lsa)) == NULL) { struct ospf_opaque_functab *functab; if ((functab = ospf_opaque_functab_lookup(lsa)) == NULL) { flog_warn( EC_OSPF_LSA, "%s: No associated function?: lsa_type(%u), opaque_type(%u)", __func__, lsa_type, opaque_type); goto out; } if ((oipt = register_opaque_info_per_type(functab, lsa)) == NULL) { flog_warn( EC_OSPF_LSA, "%s: Cannot get a control info?: lsa_type(%u), opaque_type(%u)", __func__, lsa_type, opaque_type); goto out; } } if (oipt->t_opaque_lsa_self != NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "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 = top->min_ls_interval; /* XXX */ if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d ms 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, uint8_t lsa_type, uint8_t opaque_type) { static struct ospf_lsa lsa = {0}; static struct lsa_header lsah = {0}; uint32_t tmp; lsa.oi = oi; lsa.area = area; lsa.data = &lsah; lsa.vrf_id = VRF_DEFAULT; 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 void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t) { struct opaque_info_per_type *oipt; struct ospf_opaque_functab *functab; struct ospf *top; struct ospf_interface *oi; oipt = EVENT_ARG(t); if ((functab = oipt->functab) == NULL || functab->lsa_originator == NULL) { flog_warn(EC_OSPF_LSA, "%s: No associated function?", __func__); return; } oi = (struct ospf_interface *)oipt->owner; if ((top = oi_to_top(oi)) == NULL) { flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); return; } if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE) || !OSPF_IF_PARAM(oi, opaque_capable) || !ospf_if_is_enable(oi) || ospf_nbr_count_opaque_capable(oi) == 0) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); oipt->status = PROC_SUSPEND; return; } if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Timer[Type9-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for OI (%s)", oipt->opaque_type, IF_NAME(oi)); (*functab->lsa_originator)(oi); } static void ospf_opaque_type10_lsa_reoriginate_timer(struct event *t) { struct opaque_info_per_type *oipt; struct ospf_opaque_functab *functab; struct listnode *node, *nnode; struct ospf *top; struct ospf_area *area; struct ospf_interface *oi; int n; oipt = EVENT_ARG(t); if ((functab = oipt->functab) == NULL || functab->lsa_originator == NULL) { flog_warn(EC_OSPF_LSA, "%s: No associated function?", __func__); return; } area = (struct ospf_area *)oipt->owner; if (area == NULL || (top = area->ospf) == NULL) { flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); return; } /* There must be at least one "opaque-capable, full-state" neighbor. */ n = 0; for (ALL_LIST_ELEMENTS(area->oiflist, node, nnode, oi)) { if ((n = ospf_nbr_count_opaque_capable(oi)) > 0) break; } if (n == 0 || !CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Suspend re-origination of Type-10 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); oipt->status = PROC_SUSPEND; return; } if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Timer[Type10-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for Area %pI4", oipt->opaque_type, &area->area_id); (*functab->lsa_originator)(area); } static void ospf_opaque_type11_lsa_reoriginate_timer(struct event *t) { struct opaque_info_per_type *oipt; struct ospf_opaque_functab *functab; struct ospf *top; oipt = EVENT_ARG(t); if ((functab = oipt->functab) == NULL || functab->lsa_originator == NULL) { flog_warn(EC_OSPF_LSA, "%s: No associated function?", __func__); return; } if ((top = (struct ospf *)oipt->owner) == NULL) { flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); return; } if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Suspend re-origination of Type-11 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); oipt->status = PROC_SUSPEND; return; } if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Timer[Type11-LSA]: Re-originate Opaque-LSAs (opaque-type=%u).", oipt->opaque_type); (*functab->lsa_originator)(top); } 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; struct ospf *top; int delay; if ((oipt = lookup_opaque_info_by_type(lsa0)) == NULL || (oipi = lookup_opaque_info_by_id(oipt, lsa0)) == NULL) { flog_warn(EC_OSPF_LSA, "%s: Invalid parameter?", __func__); goto out; } /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */ if ((lsa = oipi->lsa) == NULL) { flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); goto out; } if (oipi->t_opaque_lsa_self != NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "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_area(lsa->area, lsa); break; case OSPF_OPAQUE_AS_LSA: top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) top = lsa0->area->ospf; ospf_ls_retransmit_delete_nbr_as(top, lsa); break; default: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", __func__, lsa->data->type); goto out; } delay = ospf_lsa_refresh_delay(lsa); if (IS_DEBUG_OSPF_EVENT) zlog_debug( "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 * 1000); out: return; } static void ospf_opaque_lsa_refresh_timer(struct event *t) { struct opaque_info_per_id *oipi; struct ospf_opaque_functab *functab; struct ospf_lsa *lsa; if (IS_DEBUG_OSPF_EVENT) zlog_debug("Timer[Opaque-LSA]: (Opaque-LSA Refresh expire)"); oipi = EVENT_ARG(t); if ((lsa = oipi->lsa) != NULL) if ((functab = oipi->opqctl_type->functab) != NULL) if (functab->lsa_refresher != NULL) (*functab->lsa_refresher)(lsa); } 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; struct ospf *top; top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((oipt = lookup_opaque_info_by_type(lsa0)) == NULL || (oipi = lookup_opaque_info_by_id(oipt, lsa0)) == NULL) { flog_warn(EC_OSPF_LSA, "%s: Invalid parameter?", __func__); goto out; } /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */ if ((lsa = oipi->lsa) == NULL) { flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); goto out; } if (lsa->opaque_zero_len_delete && lsa->data->length != htons(sizeof(struct lsa_header))) { /* minimize the size of the withdrawal: */ /* increment the sequence number and make len just header */ /* and update checksum */ lsa->data->ls_seqnum = lsa_seqnum_increment(lsa); lsa->data->length = htons(sizeof(struct lsa_header)); lsa->data->checksum = 0; lsa->data->checksum = ospf_lsa_checksum(lsa->data); } /* 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_area(lsa->area, lsa); break; case OSPF_OPAQUE_AS_LSA: if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) top = lsa0->area->ospf; ospf_ls_retransmit_delete_nbr_as(top, lsa); break; default: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", __func__, lsa->data->type); goto out; } /* This lsa will be flushed and removed eventually. */ ospf_lsa_flush(top, lsa); /* Dequeue listnode entry from the list. */ listnode_delete(oipt->id_list, oipi); if (IS_DEBUG_OSPF_EVENT) zlog_debug( "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))); /* Disassociate internal control information with the given lsa. */ free_opaque_info_per_id((void *)oipi); out: return; } void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) { struct ospf *top; if ((top = oi_to_top(nbr->oi)) == NULL) return; if (IS_DEBUG_OSPF_EVENT) zlog_debug( "LSA[Type%d:%pI4]: processing self-originated Opaque-LSA", lsa->data->type, &lsa->data->id); /* * Install the stale LSA into the Link State Database, add it to the * MaxAge list, and flush it from the OSPF routing domain. For other * LSA types, the installation is done in the refresh function. It is * done inline here since the opaque refresh function is dynamically * registered when opaque LSAs are originated (which is not the case * for stale LSAs). */ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); ospf_lsa_install( top, (lsa->data->type == OSPF_OPAQUE_LINK_LSA) ? nbr->oi : NULL, lsa); ospf_lsa_maxage(top, lsa); switch (lsa->data->type) { case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: ospf_flood_through_area(nbr->oi->area, NULL /*inbr*/, lsa); break; case OSPF_OPAQUE_AS_LSA: ospf_flood_through_as(top, NULL /*inbr*/, lsa); break; default: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", __func__, lsa->data->type); return; } } /*------------------------------------------------------------------------* * Following are util functions; probably be used by Opaque-LSAs only... *------------------------------------------------------------------------*/ 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->ospf) == NULL) flog_warn(EC_OSPF_LSA, "Broken relationship for \"OI -> AREA -> OSPF\"?"); return top; }