// SPDX-License-Identifier: GPL-2.0-or-later /* * Server side of OSPF API. * Copyright (C) 2001, 2002 Ralph Keller * Copyright (c) 2022, LabN Consulting, L.L.C. */ #include #ifdef SUPPORT_OSPF_API #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 "buffer.h" #include #include "ospfd/ospfd.h" /* for "struct event_loop" */ #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_errors.h" #include "ospfd/ospf_memory.h" #include "ospfd/ospf_api.h" #include "ospfd/ospf_apiserver.h" DEFINE_MTYPE_STATIC(OSPFD, APISERVER, "API Server"); DEFINE_MTYPE_STATIC(OSPFD, APISERVER_MSGFILTER, "API Server Message Filter"); /* This is an implementation of an API to the OSPF daemon that allows * external applications to access the OSPF daemon through socket * connections. The application can use this API to inject its own * opaque LSAs and flood them to other OSPF daemons. Other OSPF * daemons then receive these LSAs and inform applications through the * API by sending a corresponding message. The application can also * register to receive all LSA types (in addition to opaque types) and * use this information to reconstruct the OSPF's LSDB. The OSPF * daemon supports multiple applications concurrently. */ /* List of all active connections. */ struct list *apiserver_list; /* Indicates that API the server socket local addresss has been * specified. */ struct in_addr ospf_apiserver_addr; /* ----------------------------------------------------------- * Functions to lookup interfaces * ----------------------------------------------------------- */ struct ospf_interface *ospf_apiserver_if_lookup_by_addr(struct in_addr address) { struct listnode *node, *nnode; struct ospf_interface *oi; struct ospf *ospf = NULL; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (!ospf) return NULL; for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) if (oi->type != OSPF_IFTYPE_VIRTUALLINK) if (IPV4_ADDR_SAME(&address, &oi->address->u.prefix4)) return oi; return NULL; } struct ospf_interface *ospf_apiserver_if_lookup_by_ifp(struct interface *ifp) { struct listnode *node, *nnode; struct ospf_interface *oi; struct ospf *ospf = NULL; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (!ospf) return NULL; for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) if (oi->ifp == ifp) return oi; return NULL; } /* ----------------------------------------------------------- * Initialization * ----------------------------------------------------------- */ unsigned short ospf_apiserver_getport(void) { struct servent *sp = NULL; char sbuf[16]; /* * Allow the OSPF API server port to be specified per-instance by * including the instance ID in the /etc/services name. Use the * prior name if no per-instance service is specified. */ if (ospf_instance) { snprintfrr(sbuf, sizeof(sbuf), "ospfapi-%d", ospf_instance); sp = getservbyname(sbuf, "tcp"); } if (!sp) sp = getservbyname("ospfapi", "tcp"); return sp ? ntohs(sp->s_port) : OSPF_API_SYNC_PORT; } /* Initialize OSPF API module. Invoked from ospf_opaque_init() */ int ospf_apiserver_init(void) { int fd; int rc = -1; /* Create new socket for synchronous messages. */ fd = ospf_apiserver_serv_sock_family(ospf_apiserver_getport(), AF_INET); if (fd < 0) goto out; /* Schedule new thread that handles accepted connections. */ ospf_apiserver_event(OSPF_APISERVER_ACCEPT, fd, NULL); /* Initialize list that keeps track of all connections. */ apiserver_list = list_new(); /* Register opaque-independent call back functions. These functions are invoked on ISM, NSM changes and LSA update and LSA deletes */ rc = ospf_register_opaque_functab( 0 /* all LSAs */, 0 /* all opaque types */, ospf_apiserver_new_if, ospf_apiserver_del_if, ospf_apiserver_ism_change, ospf_apiserver_nsm_change, NULL, NULL, NULL, NULL, /* ospf_apiserver_show_info */ NULL, /* originator_func */ NULL, /* ospf_apiserver_lsa_refresher */ ospf_apiserver_lsa_update, ospf_apiserver_lsa_delete); if (rc != 0) { flog_warn( EC_OSPF_OPAQUE_REGISTRATION, "ospf_apiserver_init: Failed to register opaque type [0/0]"); } rc = 0; out: return rc; } /* Terminate OSPF API module. */ void ospf_apiserver_term(void) { struct ospf_apiserver *apiserv; /* Unregister wildcard [0/0] type */ ospf_delete_opaque_functab(0 /* all LSAs */, 0 /* all opaque types */); /* * Free all client instances. ospf_apiserver_free removes the node * from the list, so we examine the head of the list anew each time. */ if (!apiserver_list) return; while (listcount(apiserver_list)) { apiserv = listgetdata(listhead(apiserver_list)); ospf_apiserver_free(apiserv); } /* Free client list itself */ if (apiserver_list) list_delete(&apiserver_list); /* Free wildcard list */ /* XXX */ } static struct ospf_apiserver *lookup_apiserver(uint8_t lsa_type, uint8_t opaque_type) { struct listnode *n1, *n2; struct registered_opaque_type *r; struct ospf_apiserver *apiserv, *found = NULL; /* XXX: this approaches O(n**2) */ for (ALL_LIST_ELEMENTS_RO(apiserver_list, n1, apiserv)) { for (ALL_LIST_ELEMENTS_RO(apiserv->opaque_types, n2, r)) if (r->lsa_type == lsa_type && r->opaque_type == opaque_type) { found = apiserv; goto out; } } out: return found; } static struct ospf_apiserver *lookup_apiserver_by_lsa(struct ospf_lsa *lsa) { struct lsa_header *lsah = lsa->data; struct ospf_apiserver *found = NULL; if (IS_OPAQUE_LSA(lsah->type)) { found = lookup_apiserver( lsah->type, GET_OPAQUE_TYPE(ntohl(lsah->id.s_addr))); } return found; } /* Allocate new connection structure. */ struct ospf_apiserver *ospf_apiserver_new(int fd_sync, int fd_async) { struct ospf_apiserver *new = XMALLOC(MTYPE_APISERVER, sizeof(struct ospf_apiserver)); new->filter = XMALLOC(MTYPE_APISERVER_MSGFILTER, sizeof(struct lsa_filter_type)); new->fd_sync = fd_sync; new->fd_async = fd_async; /* list of registered opaque types that application uses */ new->opaque_types = list_new(); /* Initialize temporary strage for LSA instances to be refreshed. */ if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: Initiallize the reserve LSDB"); memset(&new->reserve, 0, sizeof(struct ospf_lsdb)); ospf_lsdb_init(&new->reserve); new->out_sync_fifo = msg_fifo_new(); new->out_async_fifo = msg_fifo_new(); new->t_sync_read = NULL; #ifdef USE_ASYNC_READ new->t_async_read = NULL; #endif /* USE_ASYNC_READ */ new->t_sync_write = NULL; new->t_async_write = NULL; new->filter->typemask = 0; /* filter all LSAs */ new->filter->origin = ANY_ORIGIN; new->filter->num_areas = 0; return new; } void ospf_apiserver_event(enum ospf_apiserver_event event, int fd, struct ospf_apiserver *apiserv) { switch (event) { case OSPF_APISERVER_ACCEPT: (void)event_add_read(master, ospf_apiserver_accept, apiserv, fd, NULL); break; case OSPF_APISERVER_SYNC_READ: apiserv->t_sync_read = NULL; event_add_read(master, ospf_apiserver_read, apiserv, fd, &apiserv->t_sync_read); break; #ifdef USE_ASYNC_READ case OSPF_APISERVER_ASYNC_READ: apiserv->t_async_read = NULL; event_add_read(master, ospf_apiserver_read, apiserv, fd, &apiserv->t_async_read); break; #endif /* USE_ASYNC_READ */ case OSPF_APISERVER_SYNC_WRITE: event_add_write(master, ospf_apiserver_sync_write, apiserv, fd, &apiserv->t_sync_write); break; case OSPF_APISERVER_ASYNC_WRITE: event_add_write(master, ospf_apiserver_async_write, apiserv, fd, &apiserv->t_async_write); break; } } /* Free instance. First unregister all opaque types used by application, flush opaque LSAs injected by application from network and close connection. */ void ospf_apiserver_free(struct ospf_apiserver *apiserv) { struct listnode *node; /* Cancel read and write threads. */ EVENT_OFF(apiserv->t_sync_read); #ifdef USE_ASYNC_READ EVENT_OFF(apiserv->t_async_read); #endif /* USE_ASYNC_READ */ EVENT_OFF(apiserv->t_sync_write); EVENT_OFF(apiserv->t_async_write); /* Unregister all opaque types that application registered and flush opaque LSAs if still in LSDB. */ while ((node = listhead(apiserv->opaque_types)) != NULL) { struct registered_opaque_type *regtype = listgetdata(node); ospf_apiserver_unregister_opaque_type( apiserv, regtype->lsa_type, regtype->opaque_type); } list_delete(&apiserv->opaque_types); /* Close connections to OSPFd. */ if (apiserv->fd_sync > 0) { close(apiserv->fd_sync); } if (apiserv->fd_async > 0) { close(apiserv->fd_async); } /* Free fifos */ msg_fifo_free(apiserv->out_sync_fifo); msg_fifo_free(apiserv->out_async_fifo); /* Clear temporary strage for LSA instances to be refreshed. */ if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: Delete all LSAs from reserve LSDB, total=%ld", apiserv->reserve.total); ospf_lsdb_delete_all(&apiserv->reserve); ospf_lsdb_cleanup(&apiserv->reserve); /* Remove from the list of active clients. */ listnode_delete(apiserver_list, apiserv); XFREE(MTYPE_APISERVER_MSGFILTER, apiserv->filter); if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: Delete apiserv(%p), total#(%d)", (void *)apiserv, apiserver_list->count); /* And free instance. */ XFREE(MTYPE_APISERVER, apiserv); } void ospf_apiserver_read(struct event *thread) { struct ospf_apiserver *apiserv; struct msg *msg; int fd; enum ospf_apiserver_event event; apiserv = EVENT_ARG(thread); fd = EVENT_FD(thread); if (fd == apiserv->fd_sync) { event = OSPF_APISERVER_SYNC_READ; apiserv->t_sync_read = NULL; if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: %s: Peer: %pI4/%u", __func__, &apiserv->peer_sync.sin_addr, ntohs(apiserv->peer_sync.sin_port)); } #ifdef USE_ASYNC_READ else if (fd == apiserv->fd_async) { event = OSPF_APISERVER_ASYNC_READ; apiserv->t_async_read = NULL; if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: %s: Peer: %pI4/%u", __func__, &apiserv->peer_async.sin_addr, ntohs(apiserv->peer_async.sin_port)); } #endif /* USE_ASYNC_READ */ else { zlog_warn("%s: Unknown fd(%d)", __func__, fd); ospf_apiserver_free(apiserv); return; } /* Read message from fd. */ msg = msg_read(fd); if (msg == NULL) { zlog_warn("%s: read failed on fd=%d, closing connection", __func__, fd); /* Perform cleanup. */ ospf_apiserver_free(apiserv); return; } if (IS_DEBUG_OSPF_CLIENT_API) msg_print(msg); /* Dispatch to corresponding message handler. */ ospf_apiserver_handle_msg(apiserv, msg); /* Prepare for next message, add read thread. */ ospf_apiserver_event(event, fd, apiserv); msg_free(msg); } void ospf_apiserver_sync_write(struct event *thread) { struct ospf_apiserver *apiserv; struct msg *msg; int fd; int rc = -1; apiserv = EVENT_ARG(thread); assert(apiserv); fd = EVENT_FD(thread); apiserv->t_sync_write = NULL; /* Sanity check */ if (fd != apiserv->fd_sync) { zlog_warn("%s: Unknown fd=%d", __func__, fd); goto out; } if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: %s: Peer: %pI4/%u", __func__, &apiserv->peer_sync.sin_addr, ntohs(apiserv->peer_sync.sin_port)); /* Check whether there is really a message in the fifo. */ msg = msg_fifo_pop(apiserv->out_sync_fifo); if (!msg) { zlog_warn("API: %s: No message in Sync-FIFO?", __func__); return; } if (IS_DEBUG_OSPF_CLIENT_API) msg_print(msg); rc = msg_write(fd, msg); /* Once a message is dequeued, it should be freed anyway. */ msg_free(msg); if (rc < 0) { zlog_warn("%s: write failed on fd=%d", __func__, fd); goto out; } /* If more messages are in sync message fifo, schedule write thread. */ if (msg_fifo_head(apiserv->out_sync_fifo)) { ospf_apiserver_event(OSPF_APISERVER_SYNC_WRITE, apiserv->fd_sync, apiserv); } out: if (rc < 0) { /* Perform cleanup and disconnect with peer */ ospf_apiserver_free(apiserv); } } void ospf_apiserver_async_write(struct event *thread) { struct ospf_apiserver *apiserv; struct msg *msg; int fd; int rc = -1; apiserv = EVENT_ARG(thread); assert(apiserv); fd = EVENT_FD(thread); apiserv->t_async_write = NULL; /* Sanity check */ if (fd != apiserv->fd_async) { zlog_warn("%s: Unknown fd=%d", __func__, fd); goto out; } if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: %s: Peer: %pI4/%u", __func__, &apiserv->peer_async.sin_addr, ntohs(apiserv->peer_async.sin_port)); /* Check whether there is really a message in the fifo. */ msg = msg_fifo_pop(apiserv->out_async_fifo); if (!msg) { zlog_warn("API: %s: No message in Async-FIFO?", __func__); return; } if (IS_DEBUG_OSPF_CLIENT_API) msg_print(msg); rc = msg_write(fd, msg); /* Once a message is dequeued, it should be freed anyway. */ msg_free(msg); if (rc < 0) { zlog_warn("%s: write failed on fd=%d", __func__, fd); goto out; } /* If more messages are in async message fifo, schedule write thread. */ if (msg_fifo_head(apiserv->out_async_fifo)) { ospf_apiserver_event(OSPF_APISERVER_ASYNC_WRITE, apiserv->fd_async, apiserv); } out: if (rc < 0) { /* Perform cleanup and disconnect with peer */ ospf_apiserver_free(apiserv); } } int ospf_apiserver_serv_sock_family(unsigned short port, int family) { union sockunion su; int accept_sock; int rc; memset(&su, 0, sizeof(union sockunion)); su.sa.sa_family = family; /* Make new socket */ accept_sock = sockunion_stream_socket(&su); if (accept_sock < 0) return accept_sock; /* This is a server, so reuse address and port */ sockopt_reuseaddr(accept_sock); sockopt_reuseport(accept_sock); /* Bind socket to optional lcoal address and port. */ if (ospf_apiserver_addr.s_addr) sockunion2ip(&su) = ospf_apiserver_addr.s_addr; rc = sockunion_bind(accept_sock, &su, port, &su); if (rc < 0) { close(accept_sock); /* Close socket */ return rc; } /* Listen socket under queue length 3. */ rc = listen(accept_sock, 3); if (rc < 0) { zlog_warn("%s: listen: %s", __func__, safe_strerror(errno)); close(accept_sock); /* Close socket */ return rc; } return accept_sock; } /* Accept connection request from external applications. For each accepted connection allocate own connection instance. */ void ospf_apiserver_accept(struct event *thread) { int accept_sock; int new_sync_sock; int new_async_sock; union sockunion su; struct ospf_apiserver *apiserv; struct sockaddr_in peer_async; struct sockaddr_in peer_sync; unsigned int peerlen; int ret; /* EVENT_ARG (thread) is NULL */ accept_sock = EVENT_FD(thread); /* Keep hearing on socket for further connections. */ ospf_apiserver_event(OSPF_APISERVER_ACCEPT, accept_sock, NULL); memset(&su, 0, sizeof(union sockunion)); /* Accept connection for synchronous messages */ new_sync_sock = sockunion_accept(accept_sock, &su); if (new_sync_sock < 0) { zlog_warn("%s: accept: %s", __func__, safe_strerror(errno)); return; } /* Get port address and port number of peer to make reverse connection. The reverse channel uses the port number of the peer port+1. */ memset(&peer_sync, 0, sizeof(peer_sync)); peerlen = sizeof(struct sockaddr_in); ret = getpeername(new_sync_sock, (struct sockaddr *)&peer_sync, &peerlen); if (ret < 0) { zlog_warn("%s: getpeername: %s", __func__, safe_strerror(errno)); close(new_sync_sock); return; } if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: %s: New peer: %pI4/%u", __func__, &peer_sync.sin_addr, ntohs(peer_sync.sin_port)); /* Create new socket for asynchronous messages. */ peer_async = peer_sync; peer_async.sin_port = htons(ntohs(peer_sync.sin_port) + 1); /* Check if remote port number to make reverse connection is valid one. */ if (ntohs(peer_async.sin_port) == ospf_apiserver_getport()) { zlog_warn("API: %s: Peer(%pI4/%u): Invalid async port number?", __func__, &peer_async.sin_addr, ntohs(peer_async.sin_port)); close(new_sync_sock); return; } new_async_sock = socket(AF_INET, SOCK_STREAM, 0); if (new_async_sock < 0) { zlog_warn("%s: socket: %s", __func__, safe_strerror(errno)); close(new_sync_sock); return; } ret = connect(new_async_sock, (struct sockaddr *)&peer_async, sizeof(struct sockaddr_in)); if (ret < 0) { zlog_warn("%s: connect: %s", __func__, safe_strerror(errno)); close(new_sync_sock); close(new_async_sock); return; } #ifdef USE_ASYNC_READ #else /* USE_ASYNC_READ */ /* Make the asynchronous channel write-only. */ ret = shutdown(new_async_sock, SHUT_RD); if (ret < 0) { zlog_warn("%s: shutdown: %s", __func__, safe_strerror(errno)); close(new_sync_sock); close(new_async_sock); return; } #endif /* USE_ASYNC_READ */ /* Allocate new server-side connection structure */ apiserv = ospf_apiserver_new(new_sync_sock, new_async_sock); /* Add to active connection list */ listnode_add(apiserver_list, apiserv); apiserv->peer_sync = peer_sync; apiserv->peer_async = peer_async; /* And add read threads for new connection */ ospf_apiserver_event(OSPF_APISERVER_SYNC_READ, new_sync_sock, apiserv); #ifdef USE_ASYNC_READ ospf_apiserver_event(OSPF_APISERVER_ASYNC_READ, new_async_sock, apiserv); #endif /* USE_ASYNC_READ */ if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: New apiserv(%p), total#(%d)", (void *)apiserv, apiserver_list->count); } /* ----------------------------------------------------------- * Send reply with return code to client application * ----------------------------------------------------------- */ static int ospf_apiserver_send_msg(struct ospf_apiserver *apiserv, struct msg *msg) { struct msg_fifo *fifo; struct msg *msg2; enum ospf_apiserver_event event; int fd; switch (msg->hdr.msgtype) { case MSG_REPLY: fifo = apiserv->out_sync_fifo; fd = apiserv->fd_sync; event = OSPF_APISERVER_SYNC_WRITE; break; case MSG_READY_NOTIFY: case MSG_LSA_UPDATE_NOTIFY: case MSG_LSA_DELETE_NOTIFY: case MSG_NEW_IF: case MSG_DEL_IF: case MSG_ISM_CHANGE: case MSG_NSM_CHANGE: case MSG_REACHABLE_CHANGE: case MSG_ROUTER_ID_CHANGE: fifo = apiserv->out_async_fifo; fd = apiserv->fd_async; event = OSPF_APISERVER_ASYNC_WRITE; break; default: zlog_warn("%s: Unknown message type %d", __func__, msg->hdr.msgtype); return -1; } /* Make a copy of the message and put in the fifo. Once the fifo gets drained by the write thread, the message will be freed. */ /* NB: Given "msg" is untouched in this function. */ msg2 = msg_dup(msg); /* Enqueue message into corresponding fifo queue */ msg_fifo_push(fifo, msg2); /* Schedule write thread */ ospf_apiserver_event(event, fd, apiserv); return 0; } int ospf_apiserver_send_reply(struct ospf_apiserver *apiserv, uint32_t seqnr, uint8_t rc) { struct msg *msg = new_msg_reply(seqnr, rc); int ret; if (!msg) { zlog_warn("%s: msg_new failed", __func__); #ifdef NOTYET /* Cannot allocate new message. What should we do? */ ospf_apiserver_free(apiserv); #endif return -1; } ret = ospf_apiserver_send_msg(apiserv, msg); msg_free(msg); return ret; } /* ----------------------------------------------------------- * Generic message dispatching handler function * ----------------------------------------------------------- */ int ospf_apiserver_handle_msg(struct ospf_apiserver *apiserv, struct msg *msg) { int rc; /* Call corresponding message handler function. */ switch (msg->hdr.msgtype) { case MSG_REGISTER_OPAQUETYPE: rc = ospf_apiserver_handle_register_opaque_type(apiserv, msg); break; case MSG_UNREGISTER_OPAQUETYPE: rc = ospf_apiserver_handle_unregister_opaque_type(apiserv, msg); break; case MSG_REGISTER_EVENT: rc = ospf_apiserver_handle_register_event(apiserv, msg); break; case MSG_SYNC_LSDB: rc = ospf_apiserver_handle_sync_lsdb(apiserv, msg); break; case MSG_ORIGINATE_REQUEST: rc = ospf_apiserver_handle_originate_request(apiserv, msg); break; case MSG_DELETE_REQUEST: rc = ospf_apiserver_handle_delete_request(apiserv, msg); break; case MSG_SYNC_REACHABLE: rc = ospf_apiserver_handle_sync_reachable(apiserv, msg); break; case MSG_SYNC_ISM: rc = ospf_apiserver_handle_sync_ism(apiserv, msg); break; case MSG_SYNC_NSM: rc = ospf_apiserver_handle_sync_nsm(apiserv, msg); break; case MSG_SYNC_ROUTER_ID: rc = ospf_apiserver_handle_sync_router_id(apiserv, msg); break; default: zlog_warn("%s: Unknown message type: %d", __func__, msg->hdr.msgtype); rc = -1; } return rc; } /* ----------------------------------------------------------- * Following are functions for opaque type registration * ----------------------------------------------------------- */ int ospf_apiserver_register_opaque_type(struct ospf_apiserver *apiserv, uint8_t lsa_type, uint8_t opaque_type) { struct registered_opaque_type *regtype; int (*originator_func)(void *arg); int rc; switch (lsa_type) { case OSPF_OPAQUE_LINK_LSA: originator_func = ospf_apiserver_lsa9_originator; break; case OSPF_OPAQUE_AREA_LSA: originator_func = ospf_apiserver_lsa10_originator; break; case OSPF_OPAQUE_AS_LSA: originator_func = ospf_apiserver_lsa11_originator; break; default: zlog_warn("%s: lsa_type(%d)", __func__, lsa_type); return OSPF_API_ILLEGALLSATYPE; } /* Register opaque function table */ /* NB: Duplicated registration will be detected inside the function. */ rc = ospf_register_opaque_functab( lsa_type, opaque_type, NULL, /* ospf_apiserver_new_if */ NULL, /* ospf_apiserver_del_if */ NULL, /* ospf_apiserver_ism_change */ NULL, /* ospf_apiserver_nsm_change */ NULL, NULL, NULL, ospf_apiserver_show_info, originator_func, ospf_apiserver_lsa_refresher, NULL, /* ospf_apiserver_lsa_update */ NULL /* ospf_apiserver_lsa_delete */); if (rc != 0) { flog_warn(EC_OSPF_OPAQUE_REGISTRATION, "Failed to register opaque type [%d/%d]", lsa_type, opaque_type); return OSPF_API_OPAQUETYPEINUSE; } /* Remember the opaque type that application registers so when connection shuts down, we can flush all LSAs of this opaque type. */ regtype = XCALLOC(MTYPE_APISERVER, sizeof(struct registered_opaque_type)); regtype->lsa_type = lsa_type; regtype->opaque_type = opaque_type; /* Add to list of registered opaque types */ listnode_add(apiserv->opaque_types, regtype); if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug( "API: Add LSA-type(%d)/Opaque-type(%d) into apiserv(%p), total#(%d)", lsa_type, opaque_type, (void *)apiserv, listcount(apiserv->opaque_types)); return 0; } int ospf_apiserver_unregister_opaque_type(struct ospf_apiserver *apiserv, uint8_t lsa_type, uint8_t opaque_type) { struct listnode *node, *nnode; struct registered_opaque_type *regtype; for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node, nnode, regtype)) { /* Check if we really registered this opaque type */ if (regtype->lsa_type == lsa_type && regtype->opaque_type == opaque_type) { /* Yes, we registered this opaque type. Flush all existing opaque LSAs of this type */ ospf_apiserver_flush_opaque_lsa(apiserv, lsa_type, opaque_type); ospf_delete_opaque_functab(lsa_type, opaque_type); /* Remove from list of registered opaque types */ listnode_delete(apiserv->opaque_types, regtype); XFREE(MTYPE_APISERVER, regtype); if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug( "API: Del LSA-type(%d)/Opaque-type(%d) from apiserv(%p), total#(%d)", lsa_type, opaque_type, (void *)apiserv, listcount(apiserv->opaque_types)); return 0; } } /* Opaque type is not registered */ zlog_warn("Failed to unregister opaque type [%d/%d]", lsa_type, opaque_type); return OSPF_API_OPAQUETYPENOTREGISTERED; } static int apiserver_is_opaque_type_registered(struct ospf_apiserver *apiserv, uint8_t lsa_type, uint8_t opaque_type) { struct listnode *node, *nnode; struct registered_opaque_type *regtype; /* XXX: how many types are there? if few, why not just a bitmap? */ for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node, nnode, regtype)) { /* Check if we really registered this opaque type */ if (regtype->lsa_type == lsa_type && regtype->opaque_type == opaque_type) { /* Yes registered */ return 1; } } /* Not registered */ return 0; } int ospf_apiserver_handle_register_opaque_type(struct ospf_apiserver *apiserv, struct msg *msg) { struct msg_register_opaque_type *rmsg; uint8_t lsa_type; uint8_t opaque_type; int rc = 0; /* Extract parameters from register opaque type message */ rmsg = (struct msg_register_opaque_type *)STREAM_DATA(msg->s); lsa_type = rmsg->lsatype; opaque_type = rmsg->opaquetype; rc = ospf_apiserver_register_opaque_type(apiserv, lsa_type, opaque_type); /* Send a reply back to client including return code */ rc = ospf_apiserver_send_reply(apiserv, ntohl(msg->hdr.msgseq), rc); if (rc < 0) goto out; /* Now inform application about opaque types that are ready */ switch (lsa_type) { case OSPF_OPAQUE_LINK_LSA: ospf_apiserver_notify_ready_type9(apiserv); break; case OSPF_OPAQUE_AREA_LSA: ospf_apiserver_notify_ready_type10(apiserv); break; case OSPF_OPAQUE_AS_LSA: ospf_apiserver_notify_ready_type11(apiserv); break; } out: return rc; } /* Notify specific client about all opaque types 9 that are ready. */ void ospf_apiserver_notify_ready_type9(struct ospf_apiserver *apiserv) { struct listnode *node, *nnode; struct listnode *node2, *nnode2; struct ospf *ospf; struct ospf_interface *oi; struct registered_opaque_type *r; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) { /* Check if this interface is indeed ready for type 9 */ if (!ospf_apiserver_is_ready_type9(oi)) continue; /* Check for registered opaque type 9 types */ /* XXX: loop-de-loop - optimise me */ for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, r)) { struct msg *msg; if (r->lsa_type == OSPF_OPAQUE_LINK_LSA) { /* Yes, this opaque type is ready */ msg = new_msg_ready_notify( 0, OSPF_OPAQUE_LINK_LSA, r->opaque_type, oi->address->u.prefix4); if (!msg) { zlog_warn("%s: msg_new failed", __func__); #ifdef NOTYET /* Cannot allocate new message. What * should we do? */ ospf_apiserver_free(apiserv); #endif goto out; } ospf_apiserver_send_msg(apiserv, msg); msg_free(msg); } } } out: return; } /* Notify specific client about all opaque types 10 that are ready. */ void ospf_apiserver_notify_ready_type10(struct ospf_apiserver *apiserv) { struct listnode *node, *nnode; struct listnode *node2, *nnode2; struct ospf *ospf; struct ospf_area *area; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { struct registered_opaque_type *r; if (!ospf_apiserver_is_ready_type10(area)) { continue; } /* Check for registered opaque type 10 types */ /* XXX: loop in loop - optimise me */ for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, r)) { struct msg *msg; if (r->lsa_type == OSPF_OPAQUE_AREA_LSA) { /* Yes, this opaque type is ready */ msg = new_msg_ready_notify( 0, OSPF_OPAQUE_AREA_LSA, r->opaque_type, area->area_id); if (!msg) { zlog_warn("%s: msg_new failed", __func__); #ifdef NOTYET /* Cannot allocate new message. What * should we do? */ ospf_apiserver_free(apiserv); #endif goto out; } ospf_apiserver_send_msg(apiserv, msg); msg_free(msg); } } } out: return; } /* Notify specific client about all opaque types 11 that are ready */ void ospf_apiserver_notify_ready_type11(struct ospf_apiserver *apiserv) { struct listnode *node, *nnode; struct ospf *ospf; struct registered_opaque_type *r; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); /* Can type 11 be originated? */ if (!ospf_apiserver_is_ready_type11(ospf)) goto out; /* Check for registered opaque type 11 types */ for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node, nnode, r)) { struct msg *msg; struct in_addr noarea_id = {.s_addr = 0L}; if (r->lsa_type == OSPF_OPAQUE_AS_LSA) { /* Yes, this opaque type is ready */ msg = new_msg_ready_notify(0, OSPF_OPAQUE_AS_LSA, r->opaque_type, noarea_id); if (!msg) { zlog_warn("%s: msg_new failed", __func__); #ifdef NOTYET /* Cannot allocate new message. What should we * do? */ ospf_apiserver_free(apiserv); #endif goto out; } ospf_apiserver_send_msg(apiserv, msg); msg_free(msg); } } out: return; } int ospf_apiserver_handle_unregister_opaque_type(struct ospf_apiserver *apiserv, struct msg *msg) { struct msg_unregister_opaque_type *umsg; uint8_t ltype; uint8_t otype; int rc = 0; /* Extract parameters from unregister opaque type message */ umsg = (struct msg_unregister_opaque_type *)STREAM_DATA(msg->s); ltype = umsg->lsatype; otype = umsg->opaquetype; rc = ospf_apiserver_unregister_opaque_type(apiserv, ltype, otype); /* Send a reply back to client including return code */ rc = ospf_apiserver_send_reply(apiserv, ntohl(msg->hdr.msgseq), rc); return rc; } /* ----------------------------------------------------------- * Following are functions for event (filter) registration. * ----------------------------------------------------------- */ int ospf_apiserver_handle_register_event(struct ospf_apiserver *apiserv, struct msg *msg) { struct msg_register_event *rmsg; int rc; uint32_t seqnum; size_t size; rmsg = (struct msg_register_event *)STREAM_DATA(msg->s); /* Get request sequence number */ seqnum = msg_get_seq(msg); /* Free existing filter in apiserv. */ XFREE(MTYPE_APISERVER_MSGFILTER, apiserv->filter); /* Alloc new space for filter. */ size = ntohs(msg->hdr.msglen); if (size < OSPF_MAX_LSA_SIZE) { apiserv->filter = XMALLOC(MTYPE_APISERVER_MSGFILTER, size); /* copy it over. */ memcpy(apiserv->filter, &rmsg->filter, size); rc = OSPF_API_OK; } else rc = OSPF_API_NOMEMORY; /* Send a reply back to client with return code */ rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); return rc; } /* ----------------------------------------------------------- * Following are functions for LSDB synchronization. * ----------------------------------------------------------- */ static int apiserver_sync_callback(struct ospf_lsa *lsa, void *p_arg, int int_arg) { struct ospf_apiserver *apiserv; int seqnum; struct msg *msg; struct param_t { struct ospf_apiserver *apiserv; struct lsa_filter_type *filter; } * param; int rc = -1; /* Sanity check */ assert(lsa->data); assert(p_arg); param = (struct param_t *)p_arg; apiserv = param->apiserv; seqnum = (uint32_t)int_arg; /* Check origin in filter. */ if ((param->filter->origin == ANY_ORIGIN) || (param->filter->origin == (lsa->flags & OSPF_LSA_SELF))) { /* Default area for AS-External and Opaque11 LSAs */ struct in_addr area_id = {.s_addr = 0L}; /* Default interface for non Opaque9 LSAs */ struct in_addr ifaddr = {.s_addr = 0L}; if (lsa->area) { area_id = lsa->area->area_id; } if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) { ifaddr = lsa->oi->address->u.prefix4; } msg = new_msg_lsa_change_notify( MSG_LSA_UPDATE_NOTIFY, seqnum, ifaddr, area_id, lsa->flags & OSPF_LSA_SELF, lsa->data); if (!msg) { zlog_warn("%s: new_msg_update failed", __func__); #ifdef NOTYET /* Cannot allocate new message. What should we do? */ /* ospf_apiserver_free (apiserv);*/ /* Do nothing here XXX */ #endif goto out; } /* Send LSA */ ospf_apiserver_send_msg(apiserv, msg); msg_free(msg); } rc = 0; out: return rc; } int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv, struct msg *msg) { struct listnode *node, *nnode; uint32_t seqnum; int rc = 0; struct msg_sync_lsdb *smsg; struct ospf_apiserver_param_t { struct ospf_apiserver *apiserv; struct lsa_filter_type *filter; } param; uint16_t mask; struct route_node *rn; struct ospf_lsa *lsa; struct ospf *ospf; struct ospf_area *area; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); /* Get request sequence number */ seqnum = msg_get_seq(msg); /* Set sync msg. */ smsg = (struct msg_sync_lsdb *)STREAM_DATA(msg->s); /* Set parameter struct. */ param.apiserv = apiserv; param.filter = &smsg->filter; /* Remember mask. */ mask = ntohs(smsg->filter.typemask); /* Iterate over all areas. */ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { int i; uint32_t *area_id = NULL; /* Compare area_id with area_ids in sync request. */ if ((i = smsg->filter.num_areas) > 0) { /* Let area_id point to the list of area IDs, * which is at the end of smsg->filter. */ area_id = (uint32_t *)(&smsg->filter + 1); while (i) { if (*area_id == area->area_id.s_addr) { break; } i--; area_id++; } } else { i = 1; } /* If area was found, then i>0 here. */ if (i) { /* Check msg type. */ if (mask & Power2[OSPF_ROUTER_LSA]) LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) apiserver_sync_callback( lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_NETWORK_LSA]) LSDB_LOOP (NETWORK_LSDB(area), rn, lsa) apiserver_sync_callback( lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_SUMMARY_LSA]) LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) apiserver_sync_callback( lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_ASBR_SUMMARY_LSA]) LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) apiserver_sync_callback( lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_OPAQUE_LINK_LSA]) LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) apiserver_sync_callback( lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_OPAQUE_AREA_LSA]) LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) apiserver_sync_callback( lsa, (void *)¶m, seqnum); } } /* For AS-external LSAs */ if (ospf->lsdb) { if (mask & Power2[OSPF_AS_EXTERNAL_LSA]) LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) apiserver_sync_callback(lsa, (void *)¶m, seqnum); } /* For AS-external opaque LSAs */ if (ospf->lsdb) { if (mask & Power2[OSPF_OPAQUE_AS_LSA]) LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) apiserver_sync_callback(lsa, (void *)¶m, seqnum); } /* Send a reply back to client with return code */ rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); return rc; } /* * ----------------------------------------------------------- * Followings are functions for synchronization. * ----------------------------------------------------------- */ int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv, struct msg *msg) { struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); struct route_table *rt = ospf->all_rtrs; uint32_t seqnum = msg_get_seq(msg); struct in_addr *a, *abuf; struct msg_reachable_change *areach; struct msg *amsg; uint mcount, count; int _rc, rc = 0; if (!rt) goto out; /* send all adds based on current reachable routers */ a = abuf = XCALLOC(MTYPE_APISERVER, sizeof(struct in_addr) * rt->count); for (struct route_node *rn = route_top(rt); rn; rn = route_next(rn)) if (listhead((struct list *)rn->info)) *a++ = rn->p.u.prefix4; assert((a - abuf) <= (long)rt->count); count = (a - abuf); a = abuf; while (count && !rc) { amsg = new_msg_reachable_change(seqnum, count, a, 0, NULL); areach = (struct msg_reachable_change *)STREAM_DATA(amsg->s); mcount = ntohs(areach->nadd) + ntohs(areach->nremove); assert(mcount <= count); a = a + mcount; count -= mcount; rc = ospf_apiserver_send_msg(apiserv, amsg); msg_free(amsg); } XFREE(MTYPE_APISERVER, abuf); out: /* Send a reply back to client with return code */ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); rc = rc ? rc : _rc; apiserv->reachable_sync = !rc; return rc; } int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv, struct msg *msg) { struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); struct listnode *anode, *inode; struct ospf_area *area; struct ospf_interface *oi; struct msg *m; uint32_t seqnum = msg_get_seq(msg); int _rc, rc = 0; /* walk all areas */ for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) { /* walk all interfaces */ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) { m = new_msg_ism_change(seqnum, oi->address->u.prefix4, area->area_id, oi->state); rc = ospf_apiserver_send_msg(apiserv, m); msg_free(m); if (rc) break; } if (rc) break; } /* Send a reply back to client with return code */ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); return rc ? rc : _rc; } int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv, struct msg *msg) { struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); struct listnode *anode, *inode; struct ospf_area *area; struct ospf_interface *oi; struct ospf_neighbor *nbr; struct route_node *rn; struct msg *m; uint32_t seqnum = msg_get_seq(msg); int _rc, rc = 0; /* walk all areas */ for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) { /* walk all interfaces */ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) { /* walk all neighbors */ for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { nbr = rn->info; if (!nbr) continue; m = new_msg_nsm_change( seqnum, oi->address->u.prefix4, nbr->src, nbr->router_id, nbr->state); rc = ospf_apiserver_send_msg(apiserv, m); msg_free(m); if (rc) break; } if (rc) break; } if (rc) break; } /* Send a reply back to client with return code */ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); return rc ? rc : _rc; } int ospf_apiserver_handle_sync_router_id(struct ospf_apiserver *apiserv, struct msg *msg) { struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); uint32_t seqnum = msg_get_seq(msg); struct msg *m; int _rc, rc = 0; m = new_msg_router_id_change(seqnum, ospf->router_id); rc = ospf_apiserver_send_msg(apiserv, m); msg_free(m); /* Send a reply back to client with return code */ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); return rc ? rc : _rc; } /* ----------------------------------------------------------- * Following are functions to originate or update LSA * from an application. * ----------------------------------------------------------- */ /* Create a new internal opaque LSA by taking prototype and filling in missing fields such as age, sequence number, advertising router, checksum and so on. The interface parameter is used for type 9 LSAs, area parameter for type 10. Type 11 LSAs do neither need area nor interface. */ struct ospf_lsa *ospf_apiserver_opaque_lsa_new(struct ospf_area *area, struct ospf_interface *oi, struct lsa_header *protolsa) { struct stream *s; struct lsa_header *newlsa; struct ospf_lsa *new = NULL; uint8_t options = 0x0; uint16_t length; struct ospf *ospf; if (oi && oi->ospf) ospf = oi->ospf; else ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); /* Create a stream for internal opaque LSA */ if ((s = stream_new(OSPF_MAX_LSA_SIZE)) == NULL) { zlog_warn("%s: stream_new failed", __func__); return NULL; } newlsa = (struct lsa_header *)STREAM_DATA(s); /* XXX If this is a link-local LSA or an AS-external LSA, how do we have to set options? */ if (area) { options = LSA_OPTIONS_GET(area); options |= LSA_OPTIONS_NSSA_GET(area); } options |= OSPF_OPTION_O; /* Don't forget to set option bit */ if (IS_DEBUG_OSPF_CLIENT_API) { zlog_debug("LSA[Type%d:%pI4]: Creating an Opaque-LSA instance", protolsa->type, &protolsa->id); } /* Set opaque-LSA header fields. */ lsa_header_set(s, options, protolsa->type, protolsa->id, ospf->router_id); /* Set opaque-LSA body fields. */ stream_put(s, ((uint8_t *)protolsa) + sizeof(struct lsa_header), ntohs(protolsa->length) - sizeof(struct lsa_header)); /* Determine length of LSA. */ length = stream_get_endp(s); newlsa->length = htons(length); /* Create OSPF LSA. */ new = ospf_lsa_new_and_data(length); new->area = area; new->oi = oi; new->vrf_id = ospf->vrf_id; SET_FLAG(new->flags, OSPF_LSA_SELF); memcpy(new->data, newlsa, length); stream_free(s); return new; } int ospf_apiserver_is_ready_type9(struct ospf_interface *oi) { /* We can always handle getting opaque's even if we can't flood them */ return 1; } int ospf_apiserver_is_ready_type10(struct ospf_area *area) { /* We can always handle getting opaque's even if we can't flood them */ return 1; } int ospf_apiserver_is_ready_type11(struct ospf *ospf) { /* We can always handle getting opaque's even if we can't flood them */ return 1; } int ospf_apiserver_handle_originate_request(struct ospf_apiserver *apiserv, struct msg *msg) { struct msg_originate_request *omsg; struct lsa_header *data; struct ospf_lsa *new; struct ospf_lsa *old; struct ospf_area *area = NULL; struct ospf_interface *oi = NULL; struct ospf_lsdb *lsdb = NULL; struct ospf *ospf; int lsa_type, opaque_type; int ready = 0; int rc = 0; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); /* Extract opaque LSA data from message */ omsg = (struct msg_originate_request *)STREAM_DATA(msg->s); data = &omsg->data; /* Determine interface for type9 or area for type10 LSAs. */ switch (data->type) { case OSPF_OPAQUE_LINK_LSA: oi = ospf_apiserver_if_lookup_by_addr(omsg->ifaddr); if (!oi) { zlog_warn("%s: unknown interface %pI4", __func__, &omsg->ifaddr); rc = OSPF_API_NOSUCHINTERFACE; goto out; } area = oi->area; lsdb = area->lsdb; break; case OSPF_OPAQUE_AREA_LSA: area = ospf_area_lookup_by_area_id(ospf, omsg->area_id); if (!area) { zlog_warn("%s: unknown area %pI4", __func__, &omsg->area_id); rc = OSPF_API_NOSUCHAREA; goto out; } lsdb = area->lsdb; break; case OSPF_OPAQUE_AS_LSA: lsdb = ospf->lsdb; break; default: /* We can only handle opaque types here */ zlog_warn("%s: Cannot originate non-opaque LSA type %d", __func__, data->type); rc = OSPF_API_ILLEGALLSATYPE; goto out; } /* Check if we registered this opaque type */ lsa_type = data->type; opaque_type = GET_OPAQUE_TYPE(ntohl(data->id.s_addr)); if (!apiserver_is_opaque_type_registered(apiserv, lsa_type, opaque_type)) { zlog_warn("%s: LSA-type(%d)/Opaque-type(%d): Not registered", __func__, lsa_type, opaque_type); rc = OSPF_API_OPAQUETYPENOTREGISTERED; goto out; } /* Make sure that the neighbors are ready before we can originate */ switch (data->type) { case OSPF_OPAQUE_LINK_LSA: ready = ospf_apiserver_is_ready_type9(oi); break; case OSPF_OPAQUE_AREA_LSA: ready = ospf_apiserver_is_ready_type10(area); break; case OSPF_OPAQUE_AS_LSA: ready = ospf_apiserver_is_ready_type11(ospf); break; default: break; } if (!ready) { zlog_warn("Neighbors not ready to originate type %d", data->type); rc = OSPF_API_NOTREADY; goto out; } /* Create OSPF's internal opaque LSA representation */ new = ospf_apiserver_opaque_lsa_new(area, oi, data); if (!new) { rc = OSPF_API_NOMEMORY; /* XXX */ goto out; } /* Determine if LSA is new or an update for an existing one. */ old = ospf_lsdb_lookup(lsdb, new); if (!old || !ospf_opaque_is_owned(old)) { /* New LSA install in LSDB. */ rc = ospf_apiserver_originate1(new, old); } else { /* * Keep the new LSA instance in the "waiting place" until the * next * refresh timing. If several LSA update requests for the same * LSID * have issued by peer, the last one takes effect. */ new->lsdb = &apiserv->reserve; ospf_lsdb_add(&apiserv->reserve, new); if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: Add LSA(%p)[%s] into reserve LSDB, total=%ld", (void *)new, dump_lsa_key(new), new->lsdb->total); /* Kick the scheduler function. */ ospf_opaque_lsa_refresh_schedule(old); } out: /* Send a reply back to client with return code */ rc = ospf_apiserver_send_reply(apiserv, ntohl(msg->hdr.msgseq), rc); return rc; } /* ----------------------------------------------------------- * Flood an LSA within its flooding scope. * ----------------------------------------------------------- */ /* XXX We can probably use ospf_flood_through instead of this function but then we need the neighbor parameter. If we set nbr to NULL then ospf_flood_through crashes due to dereferencing NULL. */ void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa) { assert(lsa); switch (lsa->data->type) { case OSPF_OPAQUE_LINK_LSA: /* Increment counters? XXX */ /* Flood LSA through local network. */ ospf_flood_through_area(lsa->area, NULL /*nbr */, lsa); break; case OSPF_OPAQUE_AREA_LSA: /* Update LSA origination count. */ assert(lsa->area); lsa->area->ospf->lsa_originate_count++; /* Flood LSA through area. */ ospf_flood_through_area(lsa->area, NULL /*nbr */, lsa); break; case OSPF_OPAQUE_AS_LSA: { struct ospf *ospf; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); /* Increment counters? XXX */ /* Flood LSA through AS. */ ospf_flood_through_as(ospf, NULL /*nbr */, lsa); break; } } } int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old) { struct ospf *ospf; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); if (old) { /* * An old LSA exists that we didn't originate it in this * session. Dump it, but increment past it's seqnum. */ assert(!ospf_opaque_is_owned(old)); if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug( "LSA[Type%d:%pI4]: OSPF API Server Originate LSA Old Seq: 0x%x Age: %d", old->data->type, &old->data->id, ntohl(old->data->ls_seqnum), ntohl(old->data->ls_age)); if (IS_LSA_MAX_SEQ(old)) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: old LSA at maxseq", __func__); return -1; } lsa->data->ls_seqnum = lsa_seqnum_increment(old); ospf_discard_from_db(ospf, old->lsdb, old); } if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug( "LSA[Type%d:%pI4]: OSPF API Server Originate LSA New Seq: 0x%x Age: %d", lsa->data->type, &lsa->data->id, ntohl(lsa->data->ls_seqnum), ntohl(lsa->data->ls_age)); /* Install this LSA into LSDB. */ if (ospf_lsa_install(ospf, lsa->oi, lsa) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: ospf_lsa_install failed", __func__); return -1; } /* Flood LSA within scope */ #ifdef NOTYET /* * NB: Modified version of "ospf_flood_though ()" accepts NULL "inbr" * parameter, and thus it does not cause SIGSEGV error. */ ospf_flood_through(NULL /*nbr */, lsa); #else /* NOTYET */ ospf_apiserver_flood_opaque_lsa(lsa); #endif /* NOTYET */ return 0; } /* Opaque LSAs of type 9 on a specific interface can now be originated. Tell clients that registered type 9. */ int ospf_apiserver_lsa9_originator(void *arg) { struct ospf_interface *oi; oi = (struct ospf_interface *)arg; if (listcount(apiserver_list) > 0) { ospf_apiserver_clients_notify_ready_type9(oi); } return 0; } int ospf_apiserver_lsa10_originator(void *arg) { struct ospf_area *area; area = (struct ospf_area *)arg; if (listcount(apiserver_list) > 0) { ospf_apiserver_clients_notify_ready_type10(area); } return 0; } int ospf_apiserver_lsa11_originator(void *arg) { struct ospf *ospf; ospf = (struct ospf *)arg; if (listcount(apiserver_list) > 0) { ospf_apiserver_clients_notify_ready_type11(ospf); } return 0; } /* Periodically refresh opaque LSAs so that they do not expire in other routers. */ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa) { struct ospf_apiserver *apiserv; struct ospf_lsa *new = NULL; struct ospf *ospf; assert(lsa); ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); if (IS_DEBUG_OSPF_CLIENT_API) { zlog_debug("LSA[Type%d:%pI4]: OSPF API Server LSA Refresher", lsa->data->type, &lsa->data->id); } apiserv = lookup_apiserver_by_lsa(lsa); if (!apiserv) { zlog_warn("%s: LSA[%s]: No apiserver?", __func__, dump_lsa_key(lsa)); lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ goto out; } /* Check if updated version of LSA instance has already prepared. */ new = ospf_lsdb_lookup(&apiserv->reserve, lsa); if (!new) { if (IS_LSA_MAXAGE(lsa)) { ospf_opaque_lsa_flush_schedule(lsa); goto out; } /* This is a periodic refresh, driven by core OSPF mechanism. */ new = ospf_apiserver_opaque_lsa_new(lsa->area, lsa->oi, lsa->data); if (!new) { zlog_warn("%s: Cannot create a new LSA?", __func__); goto out; } } else { /* This is a forcible refresh, requested by OSPF-API client. */ if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: Delete LSA(%p)[%s] from reserve LSDB, total=%ld", (void *)new, dump_lsa_key(new), new->lsdb->total); ospf_lsdb_delete(&apiserv->reserve, new); new->lsdb = NULL; } /* Increment sequence number */ new->data->ls_seqnum = lsa_seqnum_increment(lsa); /* New LSA is in same area. */ new->area = lsa->area; SET_FLAG(new->flags, OSPF_LSA_SELF); /* Install LSA into LSDB. */ if (ospf_lsa_install(ospf, new->oi, new) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: ospf_lsa_install failed", __func__); ospf_lsa_unlock(&new); goto out; } /* Flood updated LSA through interface, area or AS */ #ifdef NOTYET ospf_flood_through(NULL /*nbr */, new); #endif /* NOTYET */ ospf_apiserver_flood_opaque_lsa(new); /* Debug logging. */ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { zlog_debug("LSA[Type%d:%pI4]: Refresh Opaque LSA", new->data->type, &new->data->id); ospf_lsa_header_dump(new->data); } out: return new; } /* ----------------------------------------------------------- * Following are functions to delete LSAs * ----------------------------------------------------------- */ int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv, struct msg *msg) { struct msg_delete_request *dmsg; struct ospf_lsa *old; struct ospf_area *area = NULL; struct ospf_interface *oi = NULL; struct in_addr id; int lsa_type, opaque_type; int rc = 0; struct ospf *ospf; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); /* Extract opaque LSA from message */ dmsg = (struct msg_delete_request *)STREAM_DATA(msg->s); /* Lookup area for link-local and area-local opaque LSAs */ switch (dmsg->lsa_type) { case OSPF_OPAQUE_LINK_LSA: oi = ospf_apiserver_if_lookup_by_addr(dmsg->addr); if (!oi) { zlog_warn("%s: unknown interface %pI4", __func__, &dmsg->addr); rc = OSPF_API_NOSUCHINTERFACE; goto out; } area = oi->area; break; case OSPF_OPAQUE_AREA_LSA: area = ospf_area_lookup_by_area_id(ospf, dmsg->addr); if (!area) { zlog_warn("%s: unknown area %pI4", __func__, &dmsg->addr); rc = OSPF_API_NOSUCHAREA; goto out; } break; case OSPF_OPAQUE_AS_LSA: /* AS-external opaque LSAs have no designated area */ area = NULL; break; default: zlog_warn("%s: Cannot delete non-opaque LSA type %d", __func__, dmsg->lsa_type); rc = OSPF_API_ILLEGALLSATYPE; goto out; } /* Check if we registered this opaque type */ lsa_type = dmsg->lsa_type; opaque_type = dmsg->opaque_type; if (!apiserver_is_opaque_type_registered(apiserv, lsa_type, opaque_type)) { zlog_warn("%s: LSA-type(%d)/Opaque-type(%d): Not registered", __func__, lsa_type, opaque_type); rc = OSPF_API_OPAQUETYPENOTREGISTERED; goto out; } /* opaque_id is in network byte order */ id.s_addr = htonl( SET_OPAQUE_LSID(dmsg->opaque_type, ntohl(dmsg->opaque_id))); /* * Even if the target LSA has once scheduled to flush, it remains in * the LSDB until it is finally handled by the maxage remover thread. * Therefore, the lookup function below may return non-NULL result. */ old = ospf_lsa_lookup(ospf, area, dmsg->lsa_type, id, ospf->router_id); if (!old) { zlog_warn("%s: LSA[Type%d:%pI4] not in LSDB", __func__, dmsg->lsa_type, &id); rc = OSPF_API_NOSUCHLSA; goto out; } if (IS_DEL_ZERO_LEN_LSA(dmsg)) { /* minimize the size of the withdrawal: */ old->opaque_zero_len_delete = 1; } /* Schedule flushing of LSA from LSDB */ /* NB: Multiple scheduling will produce a warning message, but harmless. */ ospf_opaque_lsa_flush_schedule(old); out: /* Send reply back to client including return code */ rc = ospf_apiserver_send_reply(apiserv, ntohl(msg->hdr.msgseq), rc); return rc; } /* Flush self-originated opaque LSA */ static int apiserver_flush_opaque_type_callback(struct ospf_lsa *lsa, void *p_arg, int int_arg) { struct param_t { struct ospf_apiserver *apiserv; uint8_t lsa_type; uint8_t opaque_type; } * param; /* Sanity check */ assert(lsa->data); assert(p_arg); param = (struct param_t *)p_arg; /* If LSA matches type and opaque type then delete it */ if (IS_LSA_SELF(lsa) && lsa->data->type == param->lsa_type && GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)) == param->opaque_type) { ospf_opaque_lsa_flush_schedule(lsa); } return 0; } /* Delete self-originated opaque LSAs of a given opaque type. This function is called when an application unregisters a given opaque type or a connection to an application closes and all those opaque LSAs need to be flushed the LSDB. */ void ospf_apiserver_flush_opaque_lsa(struct ospf_apiserver *apiserv, uint8_t lsa_type, uint8_t opaque_type) { struct param_t { struct ospf_apiserver *apiserv; uint8_t lsa_type; uint8_t opaque_type; } param; struct listnode *node, *nnode; struct ospf *ospf; struct ospf_area *area; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); /* Set parameter struct. */ param.apiserv = apiserv; param.lsa_type = lsa_type; param.opaque_type = opaque_type; switch (lsa_type) { struct route_node *rn; struct ospf_lsa *lsa; case OSPF_OPAQUE_LINK_LSA: for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) apiserver_flush_opaque_type_callback( lsa, (void *)¶m, 0); break; case OSPF_OPAQUE_AREA_LSA: for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) apiserver_flush_opaque_type_callback( lsa, (void *)¶m, 0); break; case OSPF_OPAQUE_AS_LSA: LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) apiserver_flush_opaque_type_callback(lsa, (void *)¶m, 0); break; default: break; } return; } /* ----------------------------------------------------------- * Following are callback functions to handle opaque types * ----------------------------------------------------------- */ int ospf_apiserver_new_if(struct interface *ifp) { struct ospf_interface *oi; /* For some strange reason it seems possible that we are invoked with an interface that has no name. This seems to happen during initialization. Return if this happens */ if (ifp->name[0] == '\0') { /* interface has empty name */ zlog_warn("%s: interface has no name?", __func__); return 0; } /* zlog_warn for debugging */ zlog_warn("ospf_apiserver_new_if"); zlog_warn("ifp name=%s status=%d index=%d", ifp->name, ifp->status, ifp->ifindex); if (ifp->name[0] == '\0') { /* interface has empty name */ zlog_warn("%s: interface has no name?", __func__); return 0; } oi = ospf_apiserver_if_lookup_by_ifp(ifp); if (!oi) { /* This interface is known to Zebra but not to OSPF daemon yet. */ zlog_warn("%s: interface %s not known to OSPFd?", __func__, ifp->name); return 0; } assert(oi); /* New interface added to OSPF, tell clients about it */ if (listcount(apiserver_list) > 0) { ospf_apiserver_clients_notify_new_if(oi); } return 0; } int ospf_apiserver_del_if(struct interface *ifp) { struct ospf_interface *oi; /* zlog_warn for debugging */ zlog_warn("%s ifp name=%s status=%d index=%d", __func__, ifp->name, ifp->status, ifp->ifindex); oi = ospf_apiserver_if_lookup_by_ifp(ifp); if (!oi) { /* This interface is known to Zebra but not to OSPF daemon anymore. No need to tell clients about it */ zlog_warn("ifp name=%s not known to OSPFd", ifp->name); return 0; } /* Interface deleted, tell clients about it */ if (listcount(apiserver_list) > 0) { ospf_apiserver_clients_notify_del_if(oi); } return 0; } void ospf_apiserver_ism_change(struct ospf_interface *oi, int old_state) { /* Tell clients about interface change */ /* zlog_warn for debugging */ zlog_warn("%s", __func__); if (listcount(apiserver_list) > 0) { ospf_apiserver_clients_notify_ism_change(oi); } zlog_warn("%s oi->ifp->name=%s old_state=%d oi->state=%d", __func__, oi->ifp->name, old_state, oi->state); } void ospf_apiserver_nsm_change(struct ospf_neighbor *nbr, int old_status) { /* Neighbor status changed, tell clients about it */ zlog_warn("%s", __func__); if (listcount(apiserver_list) > 0) { ospf_apiserver_clients_notify_nsm_change(nbr); } } void ospf_apiserver_show_info(struct vty *vty, struct json_object *json, struct ospf_lsa *lsa) { struct opaque_lsa { struct lsa_header header; uint8_t data[1]; /* opaque data have variable length. This is start address */ }; struct opaque_lsa *olsa; int opaquelen; olsa = (struct opaque_lsa *)lsa->data; if (VALID_OPAQUE_INFO_LEN(lsa->data)) opaquelen = ntohs(lsa->data->length) - OSPF_LSA_HEADER_SIZE; else opaquelen = 0; /* Output information about opaque LSAs */ if (json) json_object_string_addf(json, "opaqueData", "%*pHXn", (int)opaquelen, olsa->data); else if (vty != NULL) { int i; vty_out(vty, " Added using OSPF API: %u octets of opaque data %s\n", opaquelen, VALID_OPAQUE_INFO_LEN(lsa->data) ? "" : "(Invalid length?)"); vty_out(vty, " Opaque data: "); for (i = 0; i < opaquelen; i++) { vty_out(vty, "0x%x ", olsa->data[i]); } vty_out(vty, "\n"); } else { int i; zlog_debug( " Added using OSPF API: %u octets of opaque data %s", opaquelen, VALID_OPAQUE_INFO_LEN(lsa->data) ? "" : "(Invalid length?)"); zlog_debug(" Opaque data: "); for (i = 0; i < opaquelen; i++) { zlog_debug("0x%x ", olsa->data[i]); } } return; } /* ----------------------------------------------------------- * Following are functions to notify clients about events * ----------------------------------------------------------- */ /* Send a message to all clients. This is useful for messages that need to be notified to all clients (such as interface changes) */ void ospf_apiserver_clients_notify_all(struct msg *msg) { struct listnode *node, *nnode; struct ospf_apiserver *apiserv; /* Send message to all clients */ for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) ospf_apiserver_send_msg(apiserv, msg); } /* An interface is now ready to accept opaque LSAs. Notify all clients that registered to use this opaque type */ void ospf_apiserver_clients_notify_ready_type9(struct ospf_interface *oi) { struct listnode *node, *nnode; struct msg *msg; struct ospf_apiserver *apiserv; assert(oi); if (!oi->address) { zlog_warn("Interface has no address?"); return; } if (!ospf_apiserver_is_ready_type9(oi)) { zlog_warn("Interface not ready for type 9?"); return; } for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) { struct listnode *node2, *nnode2; struct registered_opaque_type *r; for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, r)) { if (r->lsa_type == OSPF_OPAQUE_LINK_LSA) { msg = new_msg_ready_notify( 0, OSPF_OPAQUE_LINK_LSA, r->opaque_type, oi->address->u.prefix4); if (!msg) { zlog_warn( "%s: new_msg_ready_notify failed", __func__); #ifdef NOTYET /* Cannot allocate new message. What * should we do? */ ospf_apiserver_free(apiserv); #endif goto out; } ospf_apiserver_send_msg(apiserv, msg); msg_free(msg); } } } out: return; } void ospf_apiserver_clients_notify_ready_type10(struct ospf_area *area) { struct listnode *node, *nnode; struct msg *msg; struct ospf_apiserver *apiserv; assert(area); if (!ospf_apiserver_is_ready_type10(area)) { zlog_warn("Area not ready for type 10?"); return; } for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) { struct listnode *node2, *nnode2; struct registered_opaque_type *r; for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, r)) { if (r->lsa_type == OSPF_OPAQUE_AREA_LSA) { msg = new_msg_ready_notify( 0, OSPF_OPAQUE_AREA_LSA, r->opaque_type, area->area_id); if (!msg) { zlog_warn( "%s: new_msg_ready_nofity failed", __func__); #ifdef NOTYET /* Cannot allocate new message. What * should we do? */ ospf_apiserver_free(apiserv); #endif goto out; } ospf_apiserver_send_msg(apiserv, msg); msg_free(msg); } } } out: return; } void ospf_apiserver_clients_notify_ready_type11(struct ospf *top) { struct listnode *node, *nnode; struct msg *msg; struct in_addr id_null = {.s_addr = 0L}; struct ospf_apiserver *apiserv; assert(top); if (!ospf_apiserver_is_ready_type11(top)) { zlog_warn("AS not ready for type 11?"); return; } for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) { struct listnode *node2, *nnode2; struct registered_opaque_type *r; for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, r)) { if (r->lsa_type == OSPF_OPAQUE_AS_LSA) { msg = new_msg_ready_notify( 0, OSPF_OPAQUE_AS_LSA, r->opaque_type, id_null); if (!msg) { zlog_warn( "%s: new_msg_ready_notify failed", __func__); #ifdef NOTYET /* Cannot allocate new message. What * should we do? */ ospf_apiserver_free(apiserv); #endif goto out; } ospf_apiserver_send_msg(apiserv, msg); msg_free(msg); } } } out: return; } void ospf_apiserver_clients_notify_new_if(struct ospf_interface *oi) { struct msg *msg; msg = new_msg_new_if(0, oi->address->u.prefix4, oi->area->area_id); if (msg != NULL) { ospf_apiserver_clients_notify_all(msg); msg_free(msg); } } void ospf_apiserver_clients_notify_del_if(struct ospf_interface *oi) { struct msg *msg; msg = new_msg_del_if(0, oi->address->u.prefix4); if (msg != NULL) { ospf_apiserver_clients_notify_all(msg); msg_free(msg); } } void ospf_apiserver_clients_notify_ism_change(struct ospf_interface *oi) { struct msg *msg; struct in_addr ifaddr = {.s_addr = 0L}; struct in_addr area_id = {.s_addr = 0L}; assert(oi); assert(oi->ifp); if (oi->address) { ifaddr = oi->address->u.prefix4; } if (oi->area) { area_id = oi->area->area_id; } msg = new_msg_ism_change(0, ifaddr, area_id, oi->state); if (!msg) { zlog_warn("%s: msg_new failed", __func__); return; } ospf_apiserver_clients_notify_all(msg); msg_free(msg); } void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr) { struct msg *msg; struct in_addr ifaddr; struct in_addr nbraddr; assert(nbr); ifaddr = nbr->oi->address->u.prefix4; nbraddr = nbr->address.u.prefix4; msg = new_msg_nsm_change(0, ifaddr, nbraddr, nbr->router_id, nbr->state); if (!msg) { zlog_warn("%s: msg_new failed", __func__); return; } ospf_apiserver_clients_notify_all(msg); msg_free(msg); } static int apiserver_clients_lsa_change_notify(uint8_t msgtype, struct ospf_lsa *lsa) { struct msg *msg; struct listnode *node, *nnode; struct ospf_apiserver *apiserv; /* Default area for AS-External and Opaque11 LSAs */ struct in_addr area_id = {.s_addr = 0L}; /* Default interface for non Opaque9 LSAs */ struct in_addr ifaddr = {.s_addr = 0L}; if (lsa->area) { area_id = lsa->area->area_id; } if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) { assert(lsa->oi); ifaddr = lsa->oi->address->u.prefix4; } /* Prepare message that can be sent to clients that have a matching filter */ msg = new_msg_lsa_change_notify(msgtype, 0L, /* no sequence number */ ifaddr, area_id, lsa->flags & OSPF_LSA_SELF, lsa->data); if (!msg) { zlog_warn("%s: msg_new failed", __func__); return -1; } /* Now send message to all clients with a matching filter */ for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) { struct lsa_filter_type *filter; uint16_t mask; uint32_t *area; int i; /* Check filter for this client. */ filter = apiserv->filter; /* Check area IDs in case of non AS-E LSAs. * If filter has areas (num_areas > 0), * then one of the areas must match the area ID of this LSA. */ i = filter->num_areas; if ((lsa->data->type == OSPF_AS_EXTERNAL_LSA) || (lsa->data->type == OSPF_OPAQUE_AS_LSA)) { i = 0; } if (i > 0) { area = (uint32_t *)(filter + 1); while (i) { if (*area == area_id.s_addr) { break; } i--; area++; } } else { i = 1; } if (i > 0) { /* Area match. Check LSA type. */ mask = ntohs(filter->typemask); if (mask & Power2[lsa->data->type]) { /* Type also matches. Check origin. */ if ((filter->origin == ANY_ORIGIN) || (filter->origin == IS_LSA_SELF(lsa))) { ospf_apiserver_send_msg(apiserv, msg); } } } } /* Free message since it is not used anymore */ msg_free(msg); return 0; } /* ------------------------------------------------------------- * Following are hooks invoked when LSAs are updated or deleted * ------------------------------------------------------------- */ int ospf_apiserver_lsa_update(struct ospf_lsa *lsa) { if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: LSA Update Hook Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x] Seq: 0x%x %s", lsa->data->type, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)), GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)), ntohl(lsa->data->ls_seqnum), IS_LSA_MAXAGE(lsa) ? "MaxAged " : ""); return apiserver_clients_lsa_change_notify(MSG_LSA_UPDATE_NOTIFY, lsa); } int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa) { if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("API: LSA Delete Hook Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x] Seq: 0x%x %s", lsa->data->type, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)), GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)), ntohl(lsa->data->ls_seqnum), IS_LSA_MAXAGE(lsa) ? "MaxAged " : ""); return apiserver_clients_lsa_change_notify(MSG_LSA_DELETE_NOTIFY, lsa); } /* ------------------------------------------------------------- * Reachable functions * ------------------------------------------------------------- */ static inline int cmp_route_nodes(struct route_node *orn, struct route_node *nrn) { if (!orn) return 1; else if (!nrn) return -1; uint32_t opn = ntohl(orn->p.u.prefix4.s_addr); uint32_t npn = ntohl(nrn->p.u.prefix4.s_addr); if (opn < npn) return -1; else if (opn > npn) return 1; else return 0; } void ospf_apiserver_notify_reachable(struct route_table *ort, struct route_table *nrt) { struct msg *msg; struct msg_reachable_change *areach; struct route_node *orn, *nrn; const uint insz = sizeof(struct in_addr); struct in_addr *abuf = NULL, *dbuf = NULL; struct in_addr *a = NULL, *d = NULL; uint nadd, nremove; int cmp; if (!ort && !nrt) { if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("%s: no routing tables", __func__); return; } if (nrt && nrt->count) a = abuf = XCALLOC(MTYPE_APISERVER, insz * nrt->count); if (ort && ort->count) d = dbuf = XCALLOC(MTYPE_APISERVER, insz * ort->count); /* walk both tables */ orn = ort ? route_top(ort) : NULL; nrn = nrt ? route_top(nrt) : NULL; while (orn || nrn) { if (orn && !listhead((struct list *)orn->info)) { orn = route_next(orn); continue; } if (nrn && !listhead((struct list *)nrn->info)) { nrn = route_next(nrn); continue; } cmp = cmp_route_nodes(orn, nrn); if (!cmp) { /* if old == new advance old and new */ if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("keeping router id: %pI4", &orn->p.u.prefix4); orn = route_next(orn); nrn = route_next(nrn); } else if (cmp < 0) { assert(d != NULL); /* Silence SA warning */ /* if old < new, delete old, advance old */ *d++ = orn->p.u.prefix4; if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("removing router id: %pI4", &orn->p.u.prefix4); orn = route_next(orn); } else { assert(a != NULL); /* Silence SA warning */ /* if new < old, add new, advance new */ *a++ = nrn->p.u.prefix4; if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("adding router id: %pI4", &nrn->p.u.prefix4); nrn = route_next(nrn); } } nadd = abuf ? (a - abuf) : 0; nremove = dbuf ? (d - dbuf) : 0; a = abuf; d = dbuf; while (nadd + nremove) { msg = new_msg_reachable_change(0, nadd, a, nremove, d); areach = (struct msg_reachable_change *)STREAM_DATA(msg->s); a += ntohs(areach->nadd); nadd = nadd - ntohs(areach->nadd); d += ntohs(areach->nremove); nremove = nremove - ntohs(areach->nremove); if (IS_DEBUG_OSPF_CLIENT_API) zlog_debug("%s: adding %d removing %d", __func__, ntohs(areach->nadd), ntohs(areach->nremove)); ospf_apiserver_clients_notify_all(msg); msg_free(msg); } if (abuf) XFREE(MTYPE_APISERVER, abuf); if (dbuf) XFREE(MTYPE_APISERVER, dbuf); } void ospf_apiserver_clients_notify_router_id_change(struct in_addr router_id) { struct msg *msg; msg = new_msg_router_id_change(0, router_id); if (!msg) { zlog_warn("%s: new_msg_router_id_change failed", __func__); return; } ospf_apiserver_clients_notify_all(msg); msg_free(msg); } #endif /* SUPPORT_OSPF_API */