Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
704 lines (589 sloc) 18.3 KB
/*
* Client side of OSPF API.
* Copyright (C) 2001, 2002, 2003 Ralph Keller
*
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2, or (at your
* option) any later version.
*
* GNU Zebra is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include <lib/version.h>
#include "getopt.h"
#include "thread.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"
/* 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 "ospf_apiclient.h"
/* *sigh* ... can't find a better way to hammer this into automake */
#include "ospfd/ospf_dump_api.c"
#include "ospfd/ospf_api.c"
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;
}
/* -----------------------------------------------------------
* Followings 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(struct sockaddr_in));
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(struct sockaddr_in));
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;
}
/* -----------------------------------------------------------
* Followings 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;
/* 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 = 0;
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 area_id, uint8_t lsa_type,
uint8_t opaque_type, uint32_t opaque_id)
{
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(), area_id,
lsa_type, opaque_type, opaque_id);
rc = ospf_apiclient_send_request(oclient, msg);
return rc;
}
/* -----------------------------------------------------------
* Followings 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;
int lsalen;
cn = (struct msg_lsa_change_notify *)STREAM_DATA(msg->s);
/* Extract LSA from message */
lsalen = ntohs(cn->data.length);
lsa = XMALLOC(MTYPE_OSPF_APICLIENT, lsalen);
memcpy(lsa, &(cn->data), lsalen);
/* 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, lsa);
}
static void ospf_apiclient_handle_lsa_delete(struct ospf_apiclient *oclient,
struct msg *msg)
{
struct msg_lsa_change_notify *cn;
struct lsa_header *lsa;
int lsalen;
cn = (struct msg_lsa_change_notify *)STREAM_DATA(msg->s);
/* Extract LSA from message */
lsalen = ntohs(cn->data.length);
lsa = XMALLOC(MTYPE_OSPF_APICLIENT, lsalen);
memcpy(lsa, &(cn->data), lsalen);
/* 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, lsa);
}
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;
}
You can’t perform that action at this time.