diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /ospfclient/ospf_apiclient.c | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ospfclient/ospf_apiclient.c')
-rw-r--r-- | ospfclient/ospf_apiclient.c | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/ospfclient/ospf_apiclient.c b/ospfclient/ospf_apiclient.c new file mode 100644 index 0000000..a119300 --- /dev/null +++ b/ospfclient/ospf_apiclient.c @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Client side of OSPF API. + * Copyright (C) 2001, 2002, 2003 Ralph Keller + */ + +#include <zebra.h> + +#include <lib/version.h> +#include "getopt.h" +#include "frrevent.h" +#include "prefix.h" +#include "linklist.h" +#include "if.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "filter.h" +#include "stream.h" +#include "log.h" +#include "memory.h" +#include "xref.h" + +/* work around gcc bug 69981, disable MTYPEs in libospf */ +#define _QUAGGA_OSPF_MEMORY_H + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_opaque.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_api.h" +#include "ospfd/ospf_errors.h" + +#include "ospf_apiclient.h" + +XREF_SETUP(); + +DEFINE_MGROUP(OSPFCLIENT, "libospfapiclient"); +DEFINE_MTYPE_STATIC(OSPFCLIENT, OSPF_APICLIENT, "OSPF-API client"); + +/* Backlog for listen */ +#define BACKLOG 5 + +/* ----------------------------------------------------------- + * Forward declarations + * ----------------------------------------------------------- + */ + +void ospf_apiclient_handle_reply(struct ospf_apiclient *oclient, + struct msg *msg); +void ospf_apiclient_handle_update_notify(struct ospf_apiclient *oclient, + struct msg *msg); +void ospf_apiclient_handle_delete_notify(struct ospf_apiclient *oclient, + struct msg *msg); + +/* ----------------------------------------------------------- + * Initialization + * ----------------------------------------------------------- + */ + +static unsigned short ospf_apiclient_getport(void) +{ + struct servent *sp = getservbyname("ospfapi", "tcp"); + + return sp ? ntohs(sp->s_port) : OSPF_API_SYNC_PORT; +} + +/* ----------------------------------------------------------- + * Following are functions for connection management + * ----------------------------------------------------------- + */ + +struct ospf_apiclient *ospf_apiclient_connect(char *host, int syncport) +{ + struct sockaddr_in myaddr_sync; + struct sockaddr_in myaddr_async; + struct sockaddr_in peeraddr; + struct hostent *hp; + struct ospf_apiclient *new; + int size = 0; + unsigned int peeraddrlen; + int async_server_sock; + int fd1, fd2; + int ret; + int on = 1; + + /* There are two connections between the client and the server. + First the client opens a connection for synchronous requests/replies + to the server. The server will accept this connection and + as a reaction open a reverse connection channel for + asynchronous messages. */ + + async_server_sock = socket(AF_INET, SOCK_STREAM, 0); + if (async_server_sock < 0) { + fprintf(stderr, + "ospf_apiclient_connect: creating async socket failed\n"); + return NULL; + } + + /* Prepare socket for asynchronous messages */ + /* Initialize async address structure */ + memset(&myaddr_async, 0, sizeof(myaddr_async)); + myaddr_async.sin_family = AF_INET; + myaddr_async.sin_addr.s_addr = htonl(INADDR_ANY); + myaddr_async.sin_port = htons(syncport + 1); + size = sizeof(struct sockaddr_in); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + myaddr_async.sin_len = size; +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + /* This is a server socket, reuse addr and port */ + ret = setsockopt(async_server_sock, SOL_SOCKET, SO_REUSEADDR, + (void *)&on, sizeof(on)); + if (ret < 0) { + fprintf(stderr, + "ospf_apiclient_connect: SO_REUSEADDR failed\n"); + close(async_server_sock); + return NULL; + } + +#ifdef SO_REUSEPORT + ret = setsockopt(async_server_sock, SOL_SOCKET, SO_REUSEPORT, + (void *)&on, sizeof(on)); + if (ret < 0) { + fprintf(stderr, + "ospf_apiclient_connect: SO_REUSEPORT failed\n"); + close(async_server_sock); + return NULL; + } +#endif /* SO_REUSEPORT */ + + /* Bind socket to address structure */ + ret = bind(async_server_sock, (struct sockaddr *)&myaddr_async, size); + if (ret < 0) { + fprintf(stderr, + "ospf_apiclient_connect: bind async socket failed\n"); + close(async_server_sock); + return NULL; + } + + /* Wait for reverse channel connection establishment from server */ + ret = listen(async_server_sock, BACKLOG); + if (ret < 0) { + fprintf(stderr, "ospf_apiclient_connect: listen: %s\n", + safe_strerror(errno)); + close(async_server_sock); + return NULL; + } + + /* Make connection for synchronous requests and connect to server */ + /* Resolve address of server */ + hp = gethostbyname(host); + if (!hp) { + fprintf(stderr, "ospf_apiclient_connect: no such host %s\n", + host); + close(async_server_sock); + return NULL; + } + + fd1 = socket(AF_INET, SOCK_STREAM, 0); + if (fd1 < 0) { + close(async_server_sock); + fprintf(stderr, + "ospf_apiclient_connect: creating sync socket failed\n"); + return NULL; + } + + + /* Reuse addr and port */ + ret = setsockopt(fd1, SOL_SOCKET, SO_REUSEADDR, (void *)&on, + sizeof(on)); + if (ret < 0) { + fprintf(stderr, + "ospf_apiclient_connect: SO_REUSEADDR failed\n"); + close(fd1); + close(async_server_sock); + return NULL; + } + +#ifdef SO_REUSEPORT + ret = setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, (void *)&on, + sizeof(on)); + if (ret < 0) { + fprintf(stderr, + "ospf_apiclient_connect: SO_REUSEPORT failed\n"); + close(fd1); + close(async_server_sock); + return NULL; + } +#endif /* SO_REUSEPORT */ + + + /* Bind sync socket to address structure. This is needed since we + want the sync port number on a fixed port number. The reverse + async channel will be at this port+1 */ + + memset(&myaddr_sync, 0, sizeof(myaddr_sync)); + myaddr_sync.sin_family = AF_INET; + myaddr_sync.sin_port = htons(syncport); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + myaddr_sync.sin_len = sizeof(struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + ret = bind(fd1, (struct sockaddr *)&myaddr_sync, size); + if (ret < 0) { + fprintf(stderr, + "ospf_apiclient_connect: bind sync socket failed\n"); + close(fd1); + close(async_server_sock); + return NULL; + } + + /* Prepare address structure for connect */ + memcpy(&myaddr_sync.sin_addr, hp->h_addr, hp->h_length); + myaddr_sync.sin_family = AF_INET; + myaddr_sync.sin_port = htons(ospf_apiclient_getport()); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + myaddr_sync.sin_len = sizeof(struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + /* Now establish synchronous channel with OSPF daemon */ + ret = connect(fd1, (struct sockaddr *)&myaddr_sync, + sizeof(struct sockaddr_in)); + if (ret < 0) { + fprintf(stderr, + "ospf_apiclient_connect: sync connect failed\n"); + close(async_server_sock); + close(fd1); + return NULL; + } + + /* Accept reverse connection */ + peeraddrlen = sizeof(struct sockaddr_in); + memset(&peeraddr, 0, peeraddrlen); + + fd2 = accept(async_server_sock, (struct sockaddr *)&peeraddr, + &peeraddrlen); + if (fd2 < 0) { + fprintf(stderr, + "ospf_apiclient_connect: accept async failed\n"); + close(async_server_sock); + close(fd1); + close(fd2); + return NULL; + } + + /* Server socket is not needed anymore since we are not accepting more + connections */ + close(async_server_sock); + + /* Create new client-side instance */ + new = XCALLOC(MTYPE_OSPF_APICLIENT, sizeof(struct ospf_apiclient)); + + /* Initialize socket descriptors for sync and async channels */ + new->fd_sync = fd1; + new->fd_async = fd2; + + return new; +} + +int ospf_apiclient_close(struct ospf_apiclient *oclient) +{ + + if (oclient->fd_sync >= 0) { + close(oclient->fd_sync); + } + + if (oclient->fd_async >= 0) { + close(oclient->fd_async); + } + + /* Free client structure */ + XFREE(MTYPE_OSPF_APICLIENT, oclient); + return 0; +} + +/* ----------------------------------------------------------- + * Following are functions to send a request to OSPFd + * ----------------------------------------------------------- + */ + +/* Send synchronous request, wait for reply */ +static int ospf_apiclient_send_request(struct ospf_apiclient *oclient, + struct msg *msg) +{ + uint32_t reqseq; + struct msg_reply *msgreply; + int rc; + + /* NB: Given "msg" is freed inside this function. */ + + /* Remember the sequence number of the request */ + reqseq = ntohl(msg->hdr.msgseq); + + /* Write message to OSPFd */ + rc = msg_write(oclient->fd_sync, msg); + msg_free(msg); + + if (rc < 0) { + return -1; + } + + /* Wait for reply */ /* NB: New "msg" is allocated by "msg_read()". */ + msg = msg_read(oclient->fd_sync); + if (!msg) + return -1; + + assert(msg->hdr.msgtype == MSG_REPLY); + assert(ntohl(msg->hdr.msgseq) == reqseq); + + msgreply = (struct msg_reply *)STREAM_DATA(msg->s); + rc = msgreply->errcode; + msg_free(msg); + + return rc; +} + + +/* ----------------------------------------------------------- + * Helper functions + * ----------------------------------------------------------- + */ + +static uint32_t ospf_apiclient_get_seqnr(void) +{ + static uint32_t seqnr = MIN_SEQ; + uint32_t tmp; + + tmp = seqnr; + /* Increment sequence number */ + if (seqnr < MAX_SEQ) { + seqnr++; + } else { + seqnr = MIN_SEQ; + } + return tmp; +} + +/* ----------------------------------------------------------- + * API to access OSPF daemon by client applications. + * ----------------------------------------------------------- + */ + +/* + * Synchronous request to register opaque type. + */ +int ospf_apiclient_register_opaque_type(struct ospf_apiclient *cl, + uint8_t ltype, uint8_t otype) +{ + struct msg *msg; + int rc; + + /* just put 1 as a sequence number. */ + msg = new_msg_register_opaque_type(ospf_apiclient_get_seqnr(), ltype, + otype); + if (!msg) { + fprintf(stderr, "new_msg_register_opaque_type failed\n"); + return -1; + } + + rc = ospf_apiclient_send_request(cl, msg); + return rc; +} + +/* + * Synchronous request to synchronize with OSPF's LSDB. + * Two steps required: register_event in order to get + * dynamic updates and LSDB_Sync. + */ +int ospf_apiclient_sync_lsdb(struct ospf_apiclient *oclient) +{ + struct msg *msg; + int rc; + struct lsa_filter_type filter; + + filter.typemask = 0xFFFF; /* all LSAs */ + filter.origin = ANY_ORIGIN; + filter.num_areas = 0; /* all Areas. */ + + msg = new_msg_register_event(ospf_apiclient_get_seqnr(), &filter); + if (!msg) { + fprintf(stderr, "new_msg_register_event failed\n"); + return -1; + } + rc = ospf_apiclient_send_request(oclient, msg); + + if (rc != 0) + goto out; + + msg = new_msg_sync_lsdb(ospf_apiclient_get_seqnr(), &filter); + if (!msg) { + fprintf(stderr, "new_msg_sync_lsdb failed\n"); + return -1; + } + rc = ospf_apiclient_send_request(oclient, msg); + +out: + return rc; +} + +/* + * Synchronous request to originate or update an LSA. + */ + +int ospf_apiclient_lsa_originate(struct ospf_apiclient *oclient, + struct in_addr ifaddr, struct in_addr area_id, + uint8_t lsa_type, uint8_t opaque_type, + uint32_t opaque_id, void *opaquedata, + int opaquelen) +{ + struct msg *msg; + int rc; + uint8_t buf[OSPF_MAX_LSA_SIZE]; + struct lsa_header *lsah; + uint32_t tmp; + + /* Validate opaque LSA length */ + if ((size_t)opaquelen > sizeof(buf) - sizeof(struct lsa_header)) { + fprintf(stderr, "opaquelen(%d) is larger than buf size %zu\n", + opaquelen, sizeof(buf)); + return OSPF_API_NOMEMORY; + } + + /* We can only originate opaque LSAs */ + if (!IS_OPAQUE_LSA(lsa_type)) { + fprintf(stderr, "Cannot originate non-opaque LSA type %d\n", + lsa_type); + return OSPF_API_ILLEGALLSATYPE; + } + + /* Make a new LSA from parameters */ + lsah = (struct lsa_header *)buf; + lsah->ls_age = 0; + lsah->options = 0; + lsah->type = lsa_type; + + tmp = SET_OPAQUE_LSID(opaque_type, opaque_id); + lsah->id.s_addr = htonl(tmp); + lsah->adv_router.s_addr = INADDR_ANY; + lsah->ls_seqnum = 0; + lsah->checksum = 0; + lsah->length = htons(sizeof(struct lsa_header) + opaquelen); + + memcpy(((uint8_t *)lsah) + sizeof(struct lsa_header), opaquedata, + opaquelen); + + msg = new_msg_originate_request(ospf_apiclient_get_seqnr(), ifaddr, + area_id, lsah); + if (!msg) { + fprintf(stderr, "new_msg_originate_request failed\n"); + return OSPF_API_NOMEMORY; + } + + rc = ospf_apiclient_send_request(oclient, msg); + return rc; +} + +int ospf_apiclient_lsa_delete(struct ospf_apiclient *oclient, + struct in_addr addr, uint8_t lsa_type, + uint8_t opaque_type, uint32_t opaque_id, + uint8_t flags) +{ + struct msg *msg; + int rc; + + /* Only opaque LSA can be deleted */ + if (!IS_OPAQUE_LSA(lsa_type)) { + fprintf(stderr, "Cannot delete non-opaque LSA type %d\n", + lsa_type); + return OSPF_API_ILLEGALLSATYPE; + } + + /* opaque_id is in host byte order and will be converted + * to network byte order by new_msg_delete_request */ + msg = new_msg_delete_request(ospf_apiclient_get_seqnr(), addr, lsa_type, + opaque_type, opaque_id, flags); + + rc = ospf_apiclient_send_request(oclient, msg); + return rc; +} + +/* ----------------------------------------------------------- + * Following are handlers for messages from OSPF daemon + * ----------------------------------------------------------- + */ + +static void ospf_apiclient_handle_ready(struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_ready_notify *r; + r = (struct msg_ready_notify *)STREAM_DATA(msg->s); + + /* Invoke registered callback function. */ + if (oclient->ready_notify) { + (oclient->ready_notify)(r->lsa_type, r->opaque_type, r->addr); + } +} + +static void ospf_apiclient_handle_new_if(struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_new_if *n; + n = (struct msg_new_if *)STREAM_DATA(msg->s); + + /* Invoke registered callback function. */ + if (oclient->new_if) { + (oclient->new_if)(n->ifaddr, n->area_id); + } +} + +static void ospf_apiclient_handle_del_if(struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_del_if *d; + d = (struct msg_del_if *)STREAM_DATA(msg->s); + + /* Invoke registered callback function. */ + if (oclient->del_if) { + (oclient->del_if)(d->ifaddr); + } +} + +static void ospf_apiclient_handle_ism_change(struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_ism_change *m; + m = (struct msg_ism_change *)STREAM_DATA(msg->s); + + /* Invoke registered callback function. */ + if (oclient->ism_change) { + (oclient->ism_change)(m->ifaddr, m->area_id, m->status); + } +} + +static void ospf_apiclient_handle_nsm_change(struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_nsm_change *m; + m = (struct msg_nsm_change *)STREAM_DATA(msg->s); + + /* Invoke registered callback function. */ + if (oclient->nsm_change) { + (oclient->nsm_change)(m->ifaddr, m->nbraddr, m->router_id, + m->status); + } +} + +static void ospf_apiclient_handle_lsa_update(struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_lsa_change_notify *cn; + struct lsa_header *lsa; + void *p; + uint16_t lsalen; + + cn = (struct msg_lsa_change_notify *)STREAM_DATA(msg->s); + + /* Extract LSA from message */ + lsalen = ntohs(cn->data.length); + if (lsalen > OSPF_MAX_LSA_SIZE) { + flog_warn( + EC_OSPF_LARGE_LSA, + "%s: message received size: %d is greater than a LSA size: %d", + __func__, lsalen, OSPF_MAX_LSA_SIZE); + return; + } + + p = XMALLOC(MTYPE_OSPF_APICLIENT, lsalen); + + memcpy(p, &(cn->data), lsalen); + lsa = p; + + /* Invoke registered update callback function */ + if (oclient->update_notify) { + (oclient->update_notify)(cn->ifaddr, cn->area_id, + cn->is_self_originated, lsa); + } + + /* free memory allocated by ospf apiclient library */ + XFREE(MTYPE_OSPF_APICLIENT, p); +} + +static void ospf_apiclient_handle_lsa_delete(struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_lsa_change_notify *cn; + struct lsa_header *lsa; + void *p; + uint16_t lsalen; + + cn = (struct msg_lsa_change_notify *)STREAM_DATA(msg->s); + + /* Extract LSA from message */ + lsalen = ntohs(cn->data.length); + if (lsalen > OSPF_MAX_LSA_SIZE) { + flog_warn( + EC_OSPF_LARGE_LSA, + "%s: message received size: %d is greater than a LSA size: %d", + __func__, lsalen, OSPF_MAX_LSA_SIZE); + return; + } + + p = XMALLOC(MTYPE_OSPF_APICLIENT, lsalen); + + memcpy(p, &(cn->data), lsalen); + lsa = p; + + /* Invoke registered update callback function */ + if (oclient->delete_notify) { + (oclient->delete_notify)(cn->ifaddr, cn->area_id, + cn->is_self_originated, lsa); + } + + /* free memory allocated by ospf apiclient library */ + XFREE(MTYPE_OSPF_APICLIENT, p); +} + +static void ospf_apiclient_msghandle(struct ospf_apiclient *oclient, + struct msg *msg) +{ + /* Call message handler function. */ + switch (msg->hdr.msgtype) { + case MSG_READY_NOTIFY: + ospf_apiclient_handle_ready(oclient, msg); + break; + case MSG_NEW_IF: + ospf_apiclient_handle_new_if(oclient, msg); + break; + case MSG_DEL_IF: + ospf_apiclient_handle_del_if(oclient, msg); + break; + case MSG_ISM_CHANGE: + ospf_apiclient_handle_ism_change(oclient, msg); + break; + case MSG_NSM_CHANGE: + ospf_apiclient_handle_nsm_change(oclient, msg); + break; + case MSG_LSA_UPDATE_NOTIFY: + ospf_apiclient_handle_lsa_update(oclient, msg); + break; + case MSG_LSA_DELETE_NOTIFY: + ospf_apiclient_handle_lsa_delete(oclient, msg); + break; + default: + fprintf(stderr, + "ospf_apiclient_read: Unknown message type: %d\n", + msg->hdr.msgtype); + break; + } +} + +/* ----------------------------------------------------------- + * Callback handler registration + * ----------------------------------------------------------- + */ + +void ospf_apiclient_register_callback( + struct ospf_apiclient *oclient, + void (*ready_notify)(uint8_t lsa_type, uint8_t opaque_type, + struct in_addr addr), + void (*new_if)(struct in_addr ifaddr, struct in_addr area_id), + void (*del_if)(struct in_addr ifaddr), + void (*ism_change)(struct in_addr ifaddr, struct in_addr area_id, + uint8_t status), + void (*nsm_change)(struct in_addr ifaddr, struct in_addr nbraddr, + struct in_addr router_id, uint8_t status), + void (*update_notify)(struct in_addr ifaddr, struct in_addr area_id, + uint8_t self_origin, struct lsa_header *lsa), + void (*delete_notify)(struct in_addr ifaddr, struct in_addr area_id, + uint8_t self_origin, struct lsa_header *lsa)) +{ + assert(oclient); + assert(update_notify); + + /* Register callback function */ + oclient->ready_notify = ready_notify; + oclient->new_if = new_if; + oclient->del_if = del_if; + oclient->ism_change = ism_change; + oclient->nsm_change = nsm_change; + oclient->update_notify = update_notify; + oclient->delete_notify = delete_notify; +} + +/* ----------------------------------------------------------- + * Asynchronous message handling + * ----------------------------------------------------------- + */ + +int ospf_apiclient_handle_async(struct ospf_apiclient *oclient) +{ + struct msg *msg; + + /* Get a message */ + msg = msg_read(oclient->fd_async); + + if (!msg) { + /* Connection broke down */ + return -1; + } + + /* Handle message */ + ospf_apiclient_msghandle(oclient, msg); + + /* Don't forget to free this message */ + msg_free(msg); + + return 0; +} |