// SPDX-License-Identifier: GPL-2.0-or-later /* * Client side of OSPF API. * Copyright (C) 2001, 2002, 2003 Ralph Keller */ #include #include #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; }