diff options
author | paul <paul> | 2003-03-17 01:10:58 +0000 |
---|---|---|
committer | paul <paul> | 2003-03-17 01:10:58 +0000 |
commit | 2d33f157898e50c2855cd014a9f50696dca8a77b (patch) | |
tree | 72663785bce70ff0d6d0646e4eaf6d46b1806aad /ospfd | |
parent | f9a80b452fcfec697988d77a8309a8c62ffa6007 (diff) |
Addition of OSPF-API - Amir Guindehi <nospam.amir@datacore.ch>
Diffstat (limited to 'ospfd')
-rw-r--r-- | ospfd/ospf_api.c | 647 | ||||
-rw-r--r-- | ospfd/ospf_api.h | 357 | ||||
-rw-r--r-- | ospfd/ospf_apiserver.c | 2647 | ||||
-rw-r--r-- | ospfd/ospf_apiserver.h | 201 |
4 files changed, 3852 insertions, 0 deletions
diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c new file mode 100644 index 00000000..cd69336c --- /dev/null +++ b/ospfd/ospf_api.c @@ -0,0 +1,647 @@ +/* + * API message handling module for OSPF daemon and client. + * Copyright (C) 2001, 2002 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <zebra.h> + +#ifdef SUPPORT_OSPF_API +#ifndef HAVE_OPAQUE_LSA +#error "Core Opaque-LSA module must be configured." +#endif /* HAVE_OPAQUE_LSA */ + +#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 "thread.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "buffer.h" +#include "network.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" + +#include "ospfd/ospf_api.h" + + +/* For debugging only, will be removed */ +void +api_opaque_lsa_print (struct lsa_header *data) +{ + struct opaque_lsa + { + struct lsa_header header; + u_char mydata[0]; + }; + + struct opaque_lsa *olsa; + int opaquelen; + int i; + + ospf_lsa_header_dump (data); + + olsa = (struct opaque_lsa *) data; + + opaquelen = ntohs (data->length) - OSPF_LSA_HEADER_SIZE; + zlog_warn ("apiserver_lsa_print: opaquelen=%d\n", opaquelen); + + for (i = 0; i < opaquelen; i++) + { + zlog_warn ("0x%x ", olsa->mydata[i]); + } + zlog_warn ("\n"); +} + +/* ----------------------------------------------------------- + * Generic messages + * ----------------------------------------------------------- + */ + +struct msg * +msg_new (u_char msgtype, void *msgbody, u_int32_t seqnum, u_int16_t msglen) +{ + struct msg *new; + + new = XMALLOC (MTYPE_OSPF_API_MSG, sizeof (struct msg)); + memset (new, 0, sizeof (struct msg)); + + new->hdr.version = OSPF_API_VERSION; + new->hdr.msgtype = msgtype; + new->hdr.msglen = htons (msglen); + new->hdr.msgseq = htonl (seqnum); + + new->s = stream_new (msglen); + assert (new->s); + stream_put (new->s, msgbody, msglen); + + return new; +} + + +/* Duplicate a message by copying content. */ +struct msg * +msg_dup (struct msg *msg) +{ + struct msg *new; + + assert (msg); + + new = msg_new (msg->hdr.msgtype, STREAM_DATA (msg->s), + ntohl (msg->hdr.msgseq), ntohs (msg->hdr.msglen)); + return new; +} + + +/* XXX only for testing, will be removed */ + +struct nametab { + int value; + const char *name; +}; + +const char * +ospf_api_typename (int msgtype) +{ + struct nametab NameTab[] = { + { MSG_REGISTER_OPAQUETYPE, "Register opaque-type", }, + { MSG_UNREGISTER_OPAQUETYPE, "Unregister opaque-type", }, + { MSG_REGISTER_EVENT, "Register event", }, + { MSG_SYNC_LSDB, "Sync LSDB", }, + { MSG_ORIGINATE_REQUEST, "Originate request", }, + { MSG_DELETE_REQUEST, "Delete request", }, + { MSG_REPLY, "Reply", }, + { MSG_READY_NOTIFY, "Ready notify", }, + { MSG_LSA_UPDATE_NOTIFY, "LSA update notify", }, + { MSG_LSA_DELETE_NOTIFY, "LSA delete notify", }, + { MSG_NEW_IF, "New interface", }, + { MSG_DEL_IF, "Del interface", }, + { MSG_ISM_CHANGE, "ISM change", }, + { MSG_NSM_CHANGE, "NSM change", }, + }; + + int i, n = sizeof (NameTab) / sizeof (NameTab[0]); + const char *name = NULL; + + for (i = 0; i < n; i++) + { + if (NameTab[i].value == msgtype) + { + name = NameTab[i].name; + break; + } + } + + return name ? name : "?"; +} + +const char * +ospf_api_errname (int errcode) +{ + struct nametab NameTab[] = { + { OSPF_API_OK, "OK", }, + { OSPF_API_NOSUCHINTERFACE, "No such interface", }, + { OSPF_API_NOSUCHAREA, "No such area", }, + { OSPF_API_NOSUCHLSA, "No such LSA", }, + { OSPF_API_ILLEGALLSATYPE, "Illegal LSA type", }, + { OSPF_API_OPAQUETYPEINUSE, "Opaque type in use", }, + { OSPF_API_OPAQUETYPENOTREGISTERED, "Opaque type not registered", }, + { OSPF_API_NOTREADY, "Not ready", }, + { OSPF_API_NOMEMORY, "No memory", }, + { OSPF_API_ERROR, "Other error", }, + { OSPF_API_UNDEF, "Undefined", }, + }; + + int i, n = sizeof (NameTab) / sizeof (NameTab[0]); + const char *name = NULL; + + for (i = 0; i < n; i++) + { + if (NameTab[i].value == errcode) + { + name = NameTab[i].name; + break; + } + } + + return name ? name : "?"; +} + +void +msg_print (struct msg *msg) +{ + if (!msg) + { + zlog_warn ("msg_print msg=NULL!\n"); + return; + } + +#ifdef ORIGINAL_CODING + zlog_warn + ("msg=%p msgtype=%d msglen=%d msgseq=%d streamdata=%p streamsize=%lu\n", + msg, msg->hdr.msgtype, ntohs (msg->hdr.msglen), ntohl (msg->hdr.msgseq), + STREAM_DATA (msg->s), STREAM_SIZE (msg->s)); +#else /* ORIGINAL_CODING */ + /* API message common header part. */ + zlog_info + ("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%lu)", + ospf_api_typename (msg->hdr.msgtype), msg->hdr.msgtype, + ntohs (msg->hdr.msglen), (unsigned long) ntohl (msg->hdr.msgseq), + STREAM_DATA (msg->s), STREAM_SIZE (msg->s)); + + /* API message body part. */ +#ifdef ndef + /* Generic Hex/Ascii dump */ + DumpBuf (STREAM_DATA (msg->s), STREAM_SIZE (msg->s)); /* Sorry, deleted! */ +#else /* ndef */ + /* Message-type dependent dump function. */ +#endif /* ndef */ + + return; +#endif /* ORIGINAL_CODING */ +} + +void +msg_free (struct msg *msg) +{ + if (msg->s) + stream_free (msg->s); + + XFREE (MTYPE_OSPF_API_MSG, msg); +} + + +/* Set sequence number of message */ +void +msg_set_seq (struct msg *msg, u_int32_t seqnr) +{ + assert (msg); + msg->hdr.msgseq = htonl (seqnr); +} + +/* Get sequence number of message */ +u_int32_t +msg_get_seq (struct msg *msg) +{ + assert (msg); + return ntohl (msg->hdr.msgseq); +} + +/* ----------------------------------------------------------- + * Message fifo queues + * ----------------------------------------------------------- + */ + +struct msg_fifo * +msg_fifo_new () +{ + struct msg_fifo *new; + + new = XMALLOC (MTYPE_OSPF_API_FIFO, sizeof (struct msg_fifo)); + memset (new, 0, sizeof (struct msg_fifo)); + + return new; +} + +/* Add new message to fifo. */ +void +msg_fifo_push (struct msg_fifo *fifo, struct msg *msg) +{ + if (fifo->tail) + fifo->tail->next = msg; + else + fifo->head = msg; + + fifo->tail = msg; + fifo->count++; +} + + +/* Remove first message from fifo. */ +struct msg * +msg_fifo_pop (struct msg_fifo *fifo) +{ + struct msg *msg; + + msg = fifo->head; + if (msg) + { + fifo->head = msg->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + return msg; +} + +/* Return first fifo entry but do not remove it. */ +struct msg * +msg_fifo_head (struct msg_fifo *fifo) +{ + return fifo->head; +} + +/* Flush message fifo. */ +void +msg_fifo_flush (struct msg_fifo *fifo) +{ + struct msg *op; + struct msg *next; + + for (op = fifo->head; op; op = next) + { + next = op->next; + msg_free (op); + } + + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free API message fifo. */ +void +msg_fifo_free (struct msg_fifo *fifo) +{ + msg_fifo_flush (fifo); + + XFREE (MTYPE_OSPF_API_FIFO, fifo); +} + +struct msg * +msg_read (int fd) +{ + struct msg *msg; + struct apimsghdr hdr; + char buf[OSPF_API_MAX_MSG_SIZE]; + int bodylen; + int rlen; + + /* Read message header */ + rlen = readn (fd, (char *) &hdr, sizeof (struct apimsghdr)); + + if (rlen < 0) + { + zlog_warn ("msg_read: readn %s", strerror (errno)); + return NULL; + } + else if (rlen == 0) + { + zlog_warn ("msg_read: Connection closed by peer"); + return NULL; + } + else if (rlen != sizeof (struct apimsghdr)) + { + zlog_warn ("msg_read: Cannot read message header!"); + return NULL; + } + + /* Check version of API protocol */ + if (hdr.version != OSPF_API_VERSION) + { + zlog_warn ("msg_read: OSPF API protocol version mismatch"); + return NULL; + } + + /* Determine body length. */ + bodylen = ntohs (hdr.msglen); + if (bodylen > 0) + { + + /* Read message body */ + rlen = readn (fd, buf, bodylen); + if (rlen < 0) + { + zlog_warn ("msg_read: readn %s", strerror (errno)); + return NULL; + } + else if (rlen == 0) + { + zlog_warn ("msg_read: Connection closed by peer"); + return NULL; + } + else if (rlen != bodylen) + { + zlog_warn ("msg_read: Cannot read message body!"); + return NULL; + } + } + + /* Allocate new message */ + msg = msg_new (hdr.msgtype, buf, ntohl (hdr.msgseq), ntohs (hdr.msglen)); + + return msg; +} + +int +msg_write (int fd, struct msg *msg) +{ + u_char buf[OSPF_API_MAX_MSG_SIZE]; + int l; + int wlen; + + assert (msg); + assert (msg->s); + + /* Length of message including header */ + l = sizeof (struct apimsghdr) + ntohs (msg->hdr.msglen); + + /* Make contiguous memory buffer for message */ + memcpy (buf, &msg->hdr, sizeof (struct apimsghdr)); + memcpy (buf + sizeof (struct apimsghdr), STREAM_DATA (msg->s), + ntohs (msg->hdr.msglen)); + + wlen = writen (fd, buf, l); + if (wlen < 0) + { + zlog_warn ("msg_write: writen %s", strerror (errno)); + return -1; + } + else if (wlen == 0) + { + zlog_warn ("msg_write: Connection closed by peer"); + return -1; + } + else if (wlen != l) + { + zlog_warn ("msg_write: Cannot write API message"); + return -1; + } + return 0; +} + +/* ----------------------------------------------------------- + * Specific messages + * ----------------------------------------------------------- + */ + +struct msg * +new_msg_register_opaque_type (u_int32_t seqnum, u_char ltype, u_char otype) +{ + struct msg_register_opaque_type rmsg; + + rmsg.lsatype = ltype; + rmsg.opaquetype = otype; + memset (&rmsg.pad, 0, sizeof (rmsg.pad)); + + return msg_new (MSG_REGISTER_OPAQUETYPE, &rmsg, seqnum, + sizeof (struct msg_register_opaque_type)); +} + +struct msg * +new_msg_register_event (u_int32_t seqnum, struct lsa_filter_type *filter) +{ + u_char buf[OSPF_API_MAX_MSG_SIZE]; + struct msg_register_event *emsg; + int len; + + emsg = (struct msg_register_event *) buf; + len = sizeof (struct msg_register_event) + + filter->num_areas * sizeof (struct in_addr); + emsg->filter.typemask = htons (filter->typemask); + emsg->filter.origin = filter->origin; + emsg->filter.num_areas = filter->num_areas; + return msg_new (MSG_REGISTER_EVENT, emsg, seqnum, len); +} + +struct msg * +new_msg_sync_lsdb (u_int32_t seqnum, struct lsa_filter_type *filter) +{ + u_char buf[OSPF_API_MAX_MSG_SIZE]; + struct msg_sync_lsdb *smsg; + int len; + + smsg = (struct msg_sync_lsdb *) buf; + len = sizeof (struct msg_sync_lsdb) + + filter->num_areas * sizeof (struct in_addr); + smsg->filter.typemask = htons (filter->typemask); + smsg->filter.origin = filter->origin; + smsg->filter.num_areas = filter->num_areas; + return msg_new (MSG_SYNC_LSDB, smsg, seqnum, len); +} + + +struct msg * +new_msg_originate_request (u_int32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, struct lsa_header *data) +{ + struct msg_originate_request *omsg; + int omsglen; + char buf[OSPF_API_MAX_MSG_SIZE]; + + omsglen = sizeof (struct msg_originate_request) - sizeof (struct lsa_header) + + ntohs (data->length); + + omsg = (struct msg_originate_request *) buf; + omsg->ifaddr = ifaddr; + omsg->area_id = area_id; + memcpy (&omsg->data, data, ntohs (data->length)); + + return msg_new (MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen); +} + +struct msg * +new_msg_delete_request (u_int32_t seqnum, + struct in_addr area_id, u_char lsa_type, + u_char opaque_type, u_int32_t opaque_id) +{ + struct msg_delete_request dmsg; + dmsg.area_id = area_id; + dmsg.lsa_type = lsa_type; + dmsg.opaque_type = opaque_type; + dmsg.opaque_id = htonl (opaque_id); + memset (&dmsg.pad, 0, sizeof (dmsg.pad)); + + return msg_new (MSG_DELETE_REQUEST, &dmsg, seqnum, + sizeof (struct msg_delete_request)); +} + + +struct msg * +new_msg_reply (u_int32_t seqnr, u_char rc) +{ + struct msg *msg; + struct msg_reply rmsg; + + /* Set return code */ + rmsg.errcode = rc; + memset (&rmsg.pad, 0, sizeof (rmsg.pad)); + + msg = msg_new (MSG_REPLY, &rmsg, seqnr, sizeof (struct msg_reply)); + + return msg; +} + +struct msg * +new_msg_ready_notify (u_int32_t seqnr, u_char lsa_type, + u_char opaque_type, struct in_addr addr) +{ + struct msg_ready_notify rmsg; + + rmsg.lsa_type = lsa_type; + rmsg.opaque_type = opaque_type; + memset (&rmsg.pad, 0, sizeof (rmsg.pad)); + rmsg.addr = addr; + + return msg_new (MSG_READY_NOTIFY, &rmsg, seqnr, + sizeof (struct msg_ready_notify)); +} + +struct msg * +new_msg_new_if (u_int32_t seqnr, + struct in_addr ifaddr, struct in_addr area_id) +{ + struct msg_new_if nmsg; + + nmsg.ifaddr = ifaddr; + nmsg.area_id = area_id; + + return msg_new (MSG_NEW_IF, &nmsg, seqnr, sizeof (struct msg_new_if)); +} + +struct msg * +new_msg_del_if (u_int32_t seqnr, struct in_addr ifaddr) +{ + struct msg_del_if dmsg; + + dmsg.ifaddr = ifaddr; + + return msg_new (MSG_DEL_IF, &dmsg, seqnr, sizeof (struct msg_del_if)); +} + +struct msg * +new_msg_ism_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr area_id, u_char status) +{ + struct msg_ism_change imsg; + + imsg.ifaddr = ifaddr; + imsg.area_id = area_id; + imsg.status = status; + memset (&imsg.pad, 0, sizeof (imsg.pad)); + + return msg_new (MSG_ISM_CHANGE, &imsg, seqnr, + sizeof (struct msg_ism_change)); +} + +struct msg * +new_msg_nsm_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr nbraddr, + struct in_addr router_id, u_char status) +{ + struct msg_nsm_change nmsg; + + nmsg.ifaddr = ifaddr; + nmsg.nbraddr = nbraddr; + nmsg.router_id = router_id; + nmsg.status = status; + memset (&nmsg.pad, 0, sizeof (nmsg.pad)); + + return msg_new (MSG_NSM_CHANGE, &nmsg, seqnr, + sizeof (struct msg_nsm_change)); +} + +struct msg * +new_msg_lsa_change_notify (u_char msgtype, + u_int32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + u_char is_self_originated, struct lsa_header *data) +{ + u_char buf[OSPF_API_MAX_MSG_SIZE]; + struct msg_lsa_change_notify *nmsg; + int len; + + assert (data); + + nmsg = (struct msg_lsa_change_notify *) buf; + len = ntohs (data->length) + sizeof (struct msg_lsa_change_notify) + - sizeof (struct lsa_header); + nmsg->ifaddr = ifaddr; + nmsg->area_id = area_id; + nmsg->is_self_originated = is_self_originated; + memset (&nmsg->pad, 0, sizeof (nmsg->pad)); + memcpy (&nmsg->data, data, ntohs (data->length)); + + return msg_new (msgtype, nmsg, seqnum, len); +} + +#endif /* SUPPORT_OSPF_API */ diff --git a/ospfd/ospf_api.h b/ospfd/ospf_api.h new file mode 100644 index 00000000..e7867614 --- /dev/null +++ b/ospfd/ospf_api.h @@ -0,0 +1,357 @@ +/* + * API message handling module for OSPF daemon and client. + * Copyright (C) 2001, 2002 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +/* This file is used both by the OSPFd and client applications to + define message formats used for communication. */ + +#ifndef _OSPF_API_H +#define _OSPF_API_H + +#define OSPF_API_VERSION 1 + +/* MTYPE definition is not reflected to "memory.h". */ +#define MTYPE_OSPF_API_MSG MTYPE_TMP +#define MTYPE_OSPF_API_FIFO MTYPE_TMP + +/* Default API server port to accept connection request from client-side. */ +/* This value could be overridden by "ospfapi" entry in "/etc/services". */ +#define OSPF_API_SYNC_PORT 2607 + +/* ----------------------------------------------------------- + * Generic messages + * ----------------------------------------------------------- + */ + +/* Message header structure, fields are in network byte order and + aligned to four octets. */ +struct apimsghdr +{ + u_char version; /* OSPF API protocol version */ + u_char msgtype; /* Type of message */ + u_int16_t msglen; /* Length of message w/o header */ + u_int32_t msgseq; /* Sequence number */ +}; + +/* Message representation with header and body */ +struct msg +{ + struct msg *next; /* to link into fifo */ + + /* Message header */ + struct apimsghdr hdr; + + /* Message body */ + struct stream *s; +}; + +/* Prototypes for generic messages. */ +struct msg *msg_new (u_char msgtype, void *msgbody, + u_int32_t seqnum, u_int16_t msglen); +struct msg *msg_dup (struct msg *msg); +void msg_print (struct msg *msg); /* XXX debug only */ +void msg_free (struct msg *msg); +struct msg *msg_read (int fd); +int msg_write (int fd, struct msg *msg); + +/* For requests, the message sequence number is between MIN_SEQ and + MAX_SEQ. For notifications, the sequence number is 0. */ + +#define MIN_SEQ 1 +#define MAX_SEQ 2147483647 + +void msg_set_seq (struct msg *msg, u_int32_t seqnr); +u_int32_t msg_get_seq (struct msg *msg); + +/* ----------------------------------------------------------- + * Message fifo queues + * ----------------------------------------------------------- + */ + +/* Message queue structure. */ +struct msg_fifo +{ + unsigned long count; + + struct msg *head; + struct msg *tail; +}; + +/* Prototype for message fifo queues. */ +struct msg_fifo *msg_fifo_new (); +void msg_fifo_push (struct msg_fifo *, struct msg *msg); +struct msg *msg_fifo_pop (struct msg_fifo *fifo); +struct msg *msg_fifo_head (struct msg_fifo *fifo); +void msg_fifo_flush (struct msg_fifo *fifo); +void msg_fifo_free (struct msg_fifo *fifo); + +/* ----------------------------------------------------------- + * Specific message type and format definitions + * ----------------------------------------------------------- + */ + +/* Messages to OSPF daemon. */ +#define MSG_REGISTER_OPAQUETYPE 1 +#define MSG_UNREGISTER_OPAQUETYPE 2 +#define MSG_REGISTER_EVENT 3 +#define MSG_SYNC_LSDB 4 +#define MSG_ORIGINATE_REQUEST 5 +#define MSG_DELETE_REQUEST 6 + +/* Messages from OSPF daemon. */ +#define MSG_REPLY 10 +#define MSG_READY_NOTIFY 11 +#define MSG_LSA_UPDATE_NOTIFY 12 +#define MSG_LSA_DELETE_NOTIFY 13 +#define MSG_NEW_IF 14 +#define MSG_DEL_IF 15 +#define MSG_ISM_CHANGE 16 +#define MSG_NSM_CHANGE 17 + +struct msg_register_opaque_type +{ + u_char lsatype; + u_char opaquetype; + u_char pad[2]; /* padding */ +}; + +struct msg_unregister_opaque_type +{ + u_char lsatype; + u_char opaquetype; + u_char pad[2]; /* padding */ +}; + +/* Power2 is needed to convert LSA types into bit positions, + * see typemask below. Type definition starts at 1, so + * Power2[0] is not used. */ + + +#ifdef ORIGINAL_CODING +static const u_int16_t + Power2[] = { 0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, + 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 +}; +#else +static const u_int16_t + Power2[] = { 0, (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), + (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9), + (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14), + (1 << 15) +}; +#endif /* ORIGINAL_CODING */ + +struct lsa_filter_type +{ + u_int16_t typemask; /* bitmask for selecting LSA types (1..16) */ + u_char origin; /* selects according to origin. */ +#define NON_SELF_ORIGINATED 0 +#define SELF_ORIGINATED (OSPF_LSA_SELF) +#define ANY_ORIGIN 2 + + u_char num_areas; /* number of areas in the filter. */ + /* areas, if any, go here. */ +}; + +struct msg_register_event +{ + struct lsa_filter_type filter; +}; + +struct msg_sync_lsdb +{ + struct lsa_filter_type filter; +}; + +struct msg_originate_request +{ + /* Used for LSA type 9 otherwise ignored */ + struct in_addr ifaddr; + + /* Used for LSA type 10 otherwise ignored */ + struct in_addr area_id; + + /* LSA header and LSA-specific part */ + struct lsa_header data; +}; + +struct msg_delete_request +{ + struct in_addr area_id; /* "0.0.0.0" for AS-external opaque LSAs */ + u_char lsa_type; + u_char opaque_type; + u_char pad[2]; /* padding */ + u_int32_t opaque_id; +}; + +struct msg_reply +{ + char errcode; +#define OSPF_API_OK 0 +#define OSPF_API_NOSUCHINTERFACE (-1) +#define OSPF_API_NOSUCHAREA (-2) +#define OSPF_API_NOSUCHLSA (-3) +#define OSPF_API_ILLEGALLSATYPE (-4) +#define OSPF_API_OPAQUETYPEINUSE (-5) +#define OSPF_API_OPAQUETYPENOTREGISTERED (-6) +#define OSPF_API_NOTREADY (-7) +#define OSPF_API_NOMEMORY (-8) +#define OSPF_API_ERROR (-9) +#define OSPF_API_UNDEF (-10) + u_char pad[3]; /* padding to four byte alignment */ +}; + +/* Message to tell client application that it ospf daemon is + * ready to accept opaque LSAs for a given interface or area. */ + +struct msg_ready_notify +{ + u_char lsa_type; + u_char opaque_type; + u_char pad[2]; /* padding */ + struct in_addr addr; /* interface address or area address */ +}; + +/* These messages have a dynamic length depending on the embodied LSA. + They are aligned to four octets. msg_lsa_change_notify is used for + both LSA update and LSAs delete. */ + +struct msg_lsa_change_notify +{ + /* Used for LSA type 9 otherwise ignored */ + struct in_addr ifaddr; + /* Area ID. Not valid for AS-External and Opaque11 LSAs. */ + struct in_addr area_id; + u_char is_self_originated; /* 1 if self originated. */ + u_char pad[3]; + struct lsa_header data; +}; + +struct msg_new_if +{ + struct in_addr ifaddr; /* interface IP address */ + struct in_addr area_id; /* area this interface belongs to */ +}; + +struct msg_del_if +{ + struct in_addr ifaddr; /* interface IP address */ +}; + +struct msg_ism_change +{ + struct in_addr ifaddr; /* interface IP address */ + struct in_addr area_id; /* area this interface belongs to */ + u_char status; /* interface status (up/down) */ + u_char pad[3]; /* not used */ +}; + +struct msg_nsm_change +{ + struct in_addr ifaddr; /* attached interface */ + struct in_addr nbraddr; /* Neighbor interface address */ + struct in_addr router_id; /* Router ID of neighbor */ + u_char status; /* NSM status */ + u_char pad[3]; +}; + +/* We make use of a union to define a structure that covers all + possible API messages. This allows us to find out how much memory + needs to be reserved for the largest API message. */ +struct apimsg +{ + struct apimsghdr hdr; + union + { + struct msg_register_opaque_type register_opaque_type; + struct msg_register_event register_event; + struct msg_sync_lsdb sync_lsdb; + struct msg_originate_request originate_request; + struct msg_delete_request delete_request; + struct msg_reply reply; + struct msg_ready_notify ready_notify; + struct msg_new_if new_if; + struct msg_del_if del_if; + struct msg_ism_change ism_change; + struct msg_nsm_change nsm_change; + struct msg_lsa_change_notify lsa_change_notify; + } + u; +}; + +#define OSPF_API_MAX_MSG_SIZE (sizeof(struct apimsg) + OSPF_MAX_LSA_SIZE) + +/* ----------------------------------------------------------- + * Prototypes for specific messages + * ----------------------------------------------------------- + */ + +/* For debugging only. */ +void api_opaque_lsa_print (struct lsa_header *data); + +/* Messages sent by client */ +struct msg *new_msg_register_opaque_type (u_int32_t seqnum, u_char ltype, + u_char otype); +struct msg *new_msg_register_event (u_int32_t seqnum, + struct lsa_filter_type *filter); +struct msg *new_msg_sync_lsdb (u_int32_t seqnum, + struct lsa_filter_type *filter); +struct msg *new_msg_originate_request (u_int32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + struct lsa_header *data); +struct msg *new_msg_delete_request (u_int32_t seqnum, + struct in_addr area_id, + u_char lsa_type, + u_char opaque_type, u_int32_t opaque_id); + +/* Messages sent by OSPF daemon */ +struct msg *new_msg_reply (u_int32_t seqnum, u_char rc); + +struct msg *new_msg_ready_notify (u_int32_t seqnr, u_char lsa_type, + u_char opaque_type, struct in_addr addr); + +struct msg *new_msg_new_if (u_int32_t seqnr, + struct in_addr ifaddr, struct in_addr area); + +struct msg *new_msg_del_if (u_int32_t seqnr, struct in_addr ifaddr); + +struct msg *new_msg_ism_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr area, u_char status); + +struct msg *new_msg_nsm_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr nbraddr, + struct in_addr router_id, u_char status); + +/* msgtype is MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY */ +struct msg *new_msg_lsa_change_notify (u_char msgtype, + u_int32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + u_char is_self_originated, + struct lsa_header *data); + +/* string printing functions */ +const char *ospf_api_errname (int errcode); +const char *ospf_api_typename (int msgtype); + +#endif /* _OSPF_API_H */ diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c new file mode 100644 index 00000000..fc0713b0 --- /dev/null +++ b/ospfd/ospf_apiserver.c @@ -0,0 +1,2647 @@ +/* + * Server side of OSPF API. + * Copyright (C) 2001, 2002 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <zebra.h> + +#ifdef SUPPORT_OSPF_API +#ifndef HAVE_OPAQUE_LSA +#error "Core Opaque-LSA module must be configured." +#endif /* HAVE_OPAQUE_LSA */ + +#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 "thread.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "buffer.h" + +#include <sys/types.h> + +#include "ospfd/ospfd.h" /* for "struct thread_master" */ +#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_api.h" +#include "ospfd/ospf_apiserver.h" + +/* 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. */ +list apiserver_list; + +/* ----------------------------------------------------------- + * Functions to lookup interfaces + * ----------------------------------------------------------- + */ + +struct ospf_interface * +ospf_apiserver_if_lookup_by_addr (struct in_addr address) +{ + listnode node; + struct ospf_interface *oi; + struct ospf *ospf; + + ospf = ospf_get (); + assert (ospf); + + for (node = listhead (ospf->oiflist); node; nextnode (node)) + { + if ((oi = getdata (node)) != NULL + && 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) +{ + listnode node; + struct ospf_interface *oi; + struct ospf *ospf; + + ospf = ospf_get (); + assert (ospf); + + for (node = listhead (ospf->oiflist); node; nextnode (node)) + { + if ((oi = getdata (node)) && oi->ifp == ifp) + { + return oi; + } + } + return NULL; +} + +/* ----------------------------------------------------------- + * Initialization + * ----------------------------------------------------------- + */ + +unsigned short +ospf_apiserver_getport (void) +{ + struct servent *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) + { + zlog_warn ("ospf_apiserver_init: Failed to register opaque type [0/0]"); + } + + rc = 0; + +out: + return rc; +} + +/* Terminate OSPF API module. */ +void +ospf_apiserver_term (void) +{ + listnode node; + + /* Unregister wildcard [0/0] type */ + ospf_delete_opaque_functab (0 /* all LSAs */, + 0 /* all opaque types */); + + /* Free all client instances */ + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + ospf_apiserver_free (apiserv); + } + + /* Free client list itself */ + list_delete (apiserver_list); + + /* Free wildcard list */ + /* XXX */ +} + +static struct ospf_apiserver * +lookup_apiserver (u_char lsa_type, u_char opaque_type) +{ + listnode n1, n2; + struct registered_opaque_type *r; + struct ospf_apiserver *apiserv, *found = NULL; + + for (n1 = listhead (apiserver_list); n1; nextnode (n1)) + { + apiserv = (struct ospf_apiserver *) getdata (n1); + + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + r = (struct registered_opaque_type *) getdata (n2); + + 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; +} + +/* ----------------------------------------------------------- + * Followings are functions to manage client connections. + * ----------------------------------------------------------- + */ +static int +ospf_apiserver_new_lsa_hook (struct ospf_lsa *lsa) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("API: Put LSA(%p)[%s] into reserve, total=%ld", lsa, dump_lsa_key (lsa), lsa->lsdb->total); + return 0; +} + +static int +ospf_apiserver_del_lsa_hook (struct ospf_lsa *lsa) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("API: Get LSA(%p)[%s] from reserve, total=%ld", lsa, dump_lsa_key (lsa), lsa->lsdb->total); + return 0; +} + +/* Allocate new connection structure. */ +struct ospf_apiserver * +ospf_apiserver_new (int fd_sync, int fd_async) +{ + struct ospf_apiserver *new = + XMALLOC (MTYPE_OSPF_APISERVER, sizeof (struct ospf_apiserver)); + + new->filter = + XMALLOC (MTYPE_OSPF_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. */ + memset (&new->reserve, 0, sizeof (struct ospf_lsdb)); + ospf_lsdb_init (&new->reserve); + + new->reserve.new_lsa_hook = ospf_apiserver_new_lsa_hook; /* debug */ + new->reserve.del_lsa_hook = ospf_apiserver_del_lsa_hook; /* debug */ + + 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 event event, int fd, + struct ospf_apiserver *apiserv) +{ + struct thread *apiserver_serv_thread; + + switch (event) + { + case OSPF_APISERVER_ACCEPT: + apiserver_serv_thread = + thread_add_read (master, ospf_apiserver_accept, apiserv, fd); + break; + case OSPF_APISERVER_SYNC_READ: + apiserv->t_sync_read = + thread_add_read (master, ospf_apiserver_read, apiserv, fd); + break; +#ifdef USE_ASYNC_READ + case OSPF_APISERVER_ASYNC_READ: + apiserv->t_async_read = + thread_add_read (master, ospf_apiserver_read, apiserv, fd); + break; +#endif /* USE_ASYNC_READ */ + case OSPF_APISERVER_SYNC_WRITE: + if (!apiserv->t_sync_write) + { + apiserv->t_sync_write = + thread_add_write (master, ospf_apiserver_sync_write, apiserv, fd); + } + break; + case OSPF_APISERVER_ASYNC_WRITE: + if (!apiserv->t_async_write) + { + apiserv->t_async_write = + thread_add_write (master, ospf_apiserver_async_write, apiserv, fd); + } + 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) +{ + listnode node; + + /* Cancel read and write threads. */ + if (apiserv->t_sync_read) + { + thread_cancel (apiserv->t_sync_read); + } +#ifdef USE_ASYNC_READ + if (apiserv->t_async_read) + { + thread_cancel (apiserv->t_async_read); + } +#endif /* USE_ASYNC_READ */ + if (apiserv->t_sync_write) + { + thread_cancel (apiserv->t_sync_write); + } + + if (apiserv->t_async_write) + { + thread_cancel (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 = node->data; + + ospf_apiserver_unregister_opaque_type (apiserv, regtype->lsa_type, + regtype->opaque_type); + + } + + /* 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. */ + ospf_lsdb_delete_all (&apiserv->reserve); + ospf_lsdb_cleanup (&apiserv->reserve); + + /* Remove from the list of active clients. */ + listnode_delete (apiserver_list, apiserv); + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("API: Delete apiserv(%p), total#(%d)", apiserv, apiserver_list->count); + + /* And free instance. */ + XFREE (MTYPE_OSPF_APISERVER, apiserv); +} + +int +ospf_apiserver_read (struct thread *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + int rc = -1; + enum event event; + + apiserv = THREAD_ARG (thread); + fd = THREAD_FD (thread); + + if (fd == apiserv->fd_sync) + { + event = OSPF_APISERVER_SYNC_READ; + apiserv->t_sync_read = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("API: ospf_apiserver_read: Peer: %s/%u", + inet_ntoa (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_EVENT) + zlog_info ("API: ospf_apiserver_read: Peer: %s/%u", + inet_ntoa (apiserv->peer_async.sin_addr), + ntohs (apiserv->peer_async.sin_port)); + } +#endif /* USE_ASYNC_READ */ + else + { + zlog_warn ("ospf_apiserver_read: Unknown fd(%d)", fd); + ospf_apiserver_free (apiserv); + goto out; + } + + /* Read message from fd. */ + msg = msg_read (fd); + if (msg == NULL) + { + zlog_warn + ("ospf_apiserver_read: read failed on fd=%d, closing connection", fd); + + /* Perform cleanup. */ + ospf_apiserver_free (apiserv); + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + msg_print (msg); + + /* Dispatch to corresponding message handler. */ + rc = ospf_apiserver_handle_msg (apiserv, msg); + + /* Prepare for next message, add read thread. */ + ospf_apiserver_event (event, fd, apiserv); + + msg_free (msg); + +out: + return rc; +} + +int +ospf_apiserver_sync_write (struct thread *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + int rc = -1; + + apiserv = THREAD_ARG (thread); + assert (apiserv); + fd = THREAD_FD (thread); + + apiserv->t_sync_write = NULL; + + /* Sanity check */ + if (fd != apiserv->fd_sync) + { + zlog_warn ("ospf_apiserver_sync_write: Unknown fd=%d", fd); + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("API: ospf_apiserver_sync_write: Peer: %s/%u", + inet_ntoa (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: ospf_apiserver_sync_write: No message in Sync-FIFO?"); + return 0; + } + + if (IS_DEBUG_OSPF_EVENT) + 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 + ("ospf_apiserver_sync_write: write failed on fd=%d", 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); + } + + return rc; +} + + +int +ospf_apiserver_async_write (struct thread *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + int rc = -1; + + apiserv = THREAD_ARG (thread); + assert (apiserv); + fd = THREAD_FD (thread); + + apiserv->t_async_write = NULL; + + /* Sanity check */ + if (fd != apiserv->fd_async) + { + zlog_warn ("ospf_apiserver_async_write: Unknown fd=%d", fd); + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("API: ospf_apiserver_async_write: Peer: %s/%u", + inet_ntoa (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: ospf_apiserver_async_write: No message in Async-FIFO?"); + return 0; + } + + if (IS_DEBUG_OSPF_EVENT) + 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 + ("ospf_apiserver_async_write: write failed on fd=%d", 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); + } + + return rc; +} + + +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 address and given port. */ + rc = sockunion_bind (accept_sock, &su, port, NULL); + 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 ("ospf_apiserver_serv_sock_family: listen: %s", + 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. */ +int +ospf_apiserver_accept (struct thread *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; + int peerlen; + int ret; + + /* THREAD_ARG (thread) is NULL */ + accept_sock = THREAD_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 ("ospf_apiserver_accept: accept: %s", strerror (errno)); + return -1; + } + + /* 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(struct sockaddr_in)); + peerlen = sizeof (struct sockaddr_in); + + ret = getpeername (new_sync_sock, (struct sockaddr *)&peer_sync, &peerlen); + if (ret < 0) + { + zlog_warn ("ospf_apiserver_accept: getpeername: %s", strerror (errno)); + close (new_sync_sock); + return -1; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("API: ospf_apiserver_accept: New peer: %s/%u", + inet_ntoa (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: ospf_apiserver_accept: Peer(%s/%u): Invalid async port number?", + inet_ntoa (peer_async.sin_addr), ntohs (peer_async.sin_port)); + close (new_sync_sock); + return -1; + } + + new_async_sock = socket (AF_INET, SOCK_STREAM, 0); + if (new_async_sock < 0) + { + zlog_warn ("ospf_apiserver_accept: socket: %s", strerror (errno)); + close (new_sync_sock); + return -1; + } + + ret = connect (new_async_sock, (struct sockaddr *) &peer_async, + sizeof (struct sockaddr_in)); + + if (ret < 0) + { + zlog_warn ("ospf_apiserver_accept: connect: %s", strerror (errno)); + close (new_sync_sock); + close (new_async_sock); + return -1; + } + +#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 ("ospf_apiserver_accept: shutdown: %s", strerror (errno)); + close (new_sync_sock); + close (new_async_sock); + return -1; + } +#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_EVENT) + zlog_warn ("API: New apiserv(%p), total#(%d)", apiserv, apiserver_list->count); + + return 0; +} + + +/* ----------------------------------------------------------- + * Send reply with return code to client application + * ----------------------------------------------------------- + */ + +int +ospf_apiserver_send_msg (struct ospf_apiserver *apiserv, struct msg *msg) +{ + struct msg_fifo *fifo; + struct msg *msg2; + enum 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: + fifo = apiserv->out_async_fifo; + fd = apiserv->fd_async; + event = OSPF_APISERVER_ASYNC_WRITE; + break; + default: + zlog_warn ("ospf_apiserver_send_msg: Unknown message type %d", + 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, u_int32_t seqnr, + u_char rc) +{ + struct msg *msg = new_msg_reply (seqnr, rc); + int ret; + + if (!msg) + { + zlog_warn ("ospf_apiserver_send_reply: msg_new failed"); +#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; + default: + zlog_warn ("ospf_apiserver_handle_msg: Unknown message type: %d", + msg->hdr.msgtype); + rc = -1; + } + return rc; +} + + +/* ----------------------------------------------------------- + * Following are functions for opaque type registration + * ----------------------------------------------------------- + */ + +int +ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char 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 ("ospf_apiserver_register_opaque_type: lsa_type(%d)", + 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) + { + zlog_warn ("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 = + XMALLOC (MTYPE_OSPF_APISERVER, sizeof (struct registered_opaque_type)); + memset (regtype, 0, 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_EVENT) + zlog_info ("API: Add LSA-type(%d)/Opaque-type(%d) into apiserv(%p), total#(%d)", lsa_type, opaque_type, apiserv, listcount (apiserv->opaque_types)); + + return 0; +} + +int +ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type) +{ + listnode node; + + for (node = listhead (apiserv->opaque_types); node; nextnode (node)) + { + struct registered_opaque_type *regtype = node->data; + + /* 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); + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("API: Del LSA-type(%d)/Opaque-type(%d) from apiserv(%p), total#(%d)", lsa_type, opaque_type, 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; +} + + +int +apiserver_is_opaque_type_registered (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type) +{ + listnode node; + + for (node = listhead (apiserv->opaque_types); node; nextnode (node)) + { + struct registered_opaque_type *regtype = node->data; + + /* 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; + u_char lsa_type; + u_char 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) +{ + listnode node; + listnode n2; + + for (node = listhead (ospf_top->oiflist); node; nextnode (node)) + { + struct ospf_interface *oi = (struct ospf_interface *) getdata (node); + + /* 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 */ + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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 ("apiserver_notify_ready_type9: msg_new failed"); +#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) +{ + listnode node; + listnode n2; + + for (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = getdata (node); + + if (!ospf_apiserver_is_ready_type10 (area)) + { + continue; + } + + /* Check for registered opaque type 10 types */ + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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 ("apiserver_notify_ready_type10: msg_new failed"); +#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) +{ + listnode n2; + + /* Can type 11 be originated? */ + if (!ospf_apiserver_is_ready_type11 (ospf_top)) + goto out;; + + /* Check for registered opaque type 11 types */ + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + struct msg *msg; + struct in_addr noarea_id = { 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 ("apiserver_notify_ready_type11: msg_new failed"); +#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; + u_char ltype; + u_char 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; + u_int32_t seqnum; + + 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_OSPF_APISERVER_MSGFILTER, apiserv->filter); + /* Alloc new space for filter. */ + + apiserv->filter = XMALLOC (MTYPE_OSPF_APISERVER_MSGFILTER, + ntohs (msg->hdr.msglen)); + if (apiserv->filter) + { + /* copy it over. */ + memcpy (apiserv->filter, &rmsg->filter, ntohs (msg->hdr.msglen)); + 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; +} + + +/* ----------------------------------------------------------- + * Followings are functions for LSDB synchronization. + * ----------------------------------------------------------- + */ + +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 = (u_int32_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 = { 0L }; + + /* Default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { 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 ("apiserver_sync_callback: new_msg_update failed"); +#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) +{ + listnode node; + u_int32_t seqnum; + int rc = 0; + struct msg_sync_lsdb *smsg; + struct param_t + { + struct ospf_apiserver *apiserv; + struct lsa_filter_type *filter; + } + param; + u_int16_t mask; + + /* 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 (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = node->data; + int i; + u_int32_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 = (u_int32_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]) + foreach_lsa (ROUTER_LSDB (area), (void *) ¶m, seqnum, + apiserver_sync_callback); + if (mask & Power2[OSPF_NETWORK_LSA]) + foreach_lsa (NETWORK_LSDB (area), (void *) ¶m, seqnum, + apiserver_sync_callback); + if (mask & Power2[OSPF_SUMMARY_LSA]) + foreach_lsa (SUMMARY_LSDB (area), (void *) ¶m, seqnum, + apiserver_sync_callback); + if (mask & Power2[OSPF_ASBR_SUMMARY_LSA]) + foreach_lsa (ASBR_SUMMARY_LSDB (area), (void *) ¶m, seqnum, + apiserver_sync_callback); + if (mask & Power2[OSPF_OPAQUE_LINK_LSA]) + foreach_lsa (OPAQUE_LINK_LSDB (area), (void *) ¶m, + seqnum, apiserver_sync_callback); + if (mask & Power2[OSPF_OPAQUE_AREA_LSA]) + foreach_lsa (OPAQUE_AREA_LSDB (area), (void *) ¶m, + seqnum, apiserver_sync_callback); + } + } + + /* For AS-external LSAs */ + if (ospf_top->lsdb) + { + if (mask & Power2[OSPF_AS_EXTERNAL_LSA]) + foreach_lsa (EXTERNAL_LSDB (ospf_top), (void *) ¶m, seqnum, + apiserver_sync_callback); + } + + /* For AS-external opaque LSAs */ + if (ospf_top->lsdb) + { + if (mask & Power2[OSPF_OPAQUE_AS_LSA]) + foreach_lsa (OPAQUE_AS_LSDB (ospf_top), (void *) ¶m, + seqnum, apiserver_sync_callback); + } + + /* Send a reply back to client with return code */ + rc = ospf_apiserver_send_reply (apiserv, seqnum, rc); + return rc; +} + + +/* ----------------------------------------------------------- + * Followings 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; + u_char options = 0x0; + u_int16_t length; + + /* Create a stream for internal opaque LSA */ + if ((s = stream_new (OSPF_MAX_LSA_SIZE)) == NULL) + { + zlog_warn ("ospf_apiserver_opaque_lsa_new: stream_new failed"); + 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); +#ifdef HAVE_NSSA + options |= LSA_NSSA_GET (area); +#endif /* HAVE_NSSA */ + } + + options |= OSPF_OPTION_O; /* Don't forget to set option bit */ + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_info ("LSA[Type%d:%s]: Creating an Opaque-LSA instance", + protolsa->type, inet_ntoa (protolsa->id)); + } + + /* Set opaque-LSA header fields. */ + lsa_header_set (s, options, protolsa->type, protolsa->id); + + /* Set opaque-LSA body fields. */ + stream_put (s, ((u_char *) 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. */ + if ((new = ospf_lsa_new ()) == NULL) + { + zlog_warn ("ospf_apiserver_opaque_lsa_new: ospf_lsa_new() ?"); + stream_free (s); + return NULL; + } + + if ((new->data = ospf_lsa_data_new (length)) == NULL) + { + zlog_warn ("ospf_apiserver_opaque_lsa_new: ospf_lsa_data_new() ?"); + ospf_lsa_free (new); + new = NULL; + stream_free (s); + return NULL; + } + + new->area = area; + new->oi = oi; + + 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) +{ + /* Type 9 opaque LSA can be originated if there is at least one + active opaque-capable neighbor attached to the outgoing + interface. */ + + return (ospf_opaque_capable_nbr_count (oi->nbrs, NSM_Full) > 0); +} + +int +ospf_apiserver_is_ready_type10 (struct ospf_area *area) +{ + /* Type 10 opaque LSA can be originated if there is at least one + interface belonging to the area that has an active opaque-capable + neighbor. */ + listnode node; + + for (node = listhead (area->oiflist); node; nextnode (node)) + { + struct ospf_interface *oi = getdata (node); + + /* Is there an active neighbor attached to this interface? */ + if (ospf_apiserver_is_ready_type9 (oi)) + { + return 1; + } + } + /* No active neighbor in area */ + return 0; +} + +int +ospf_apiserver_is_ready_type11 (struct ospf *ospf) +{ + /* Type 11 opaque LSA can be originated if there is at least one interface + that has an active opaque-capable neighbor. */ + listnode node; + + for (node = listhead (ospf->oiflist); node; nextnode (node)) + { + struct ospf_interface *oi = getdata (node); + + /* Is there an active neighbor attached to this interface? */ + if (ospf_apiserver_is_ready_type9 (oi)) + return 1; + } + /* No active neighbor at all */ + return 0; +} + + +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; + int lsa_type, opaque_type; + int ready = 0; + int rc = 0; + + /* 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 ("apiserver_originate: unknown interface %s", + inet_ntoa (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 (omsg->area_id); + if (!area) + { + zlog_warn ("apiserver_originate: unknown area %s", + inet_ntoa (omsg->area_id)); + rc = OSPF_API_NOSUCHAREA; + goto out; + } + lsdb = area->lsdb; + break; + case OSPF_OPAQUE_AS_LSA: + lsdb = ospf_top->lsdb; + break; + default: + /* We can only handle opaque types here */ + zlog_warn ("apiserver_originate: Cannot originate non-opaque LSA type %d", + 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 ("apiserver_originate: LSA-type(%d)/Opaque-type(%d): Not registered", 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_top); + 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) + { + /* New LSA install in LSDB. */ + rc = ospf_apiserver_originate1 (new); + } + 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); + + /* 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->top->lsa_originate_count++; + + /* Flood LSA through area. */ + ospf_flood_through_area (lsa->area, NULL /*nbr */ , lsa); + break; + case OSPF_OPAQUE_AS_LSA: + /* Increment counters? XXX */ + + /* Flood LSA through AS. */ + ospf_flood_through_as (NULL /*nbr */ , lsa); + break; + } +} + +int +ospf_apiserver_originate1 (struct ospf_lsa *lsa) +{ + /* Install this LSA into LSDB. */ + if (ospf_lsa_install (lsa->oi, lsa) == NULL) + { + zlog_warn ("ospf_apiserver_originate1: ospf_lsa_install failed"); + 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. */ +void +ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa) +{ + struct ospf_apiserver *apiserv; + struct ospf_lsa *new = NULL; + + apiserv = lookup_apiserver_by_lsa (lsa); + if (!apiserv) + { + zlog_warn ("ospf_apiserver_lsa_refresher: LSA[%s]: No apiserver?", dump_lsa_key (lsa)); + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */ + } + + if (IS_LSA_MAXAGE (lsa)) + { + ospf_opaque_lsa_flush_schedule (lsa); + goto out; + } + + /* Check if updated version of LSA instance has already prepared. */ + new = ospf_lsdb_lookup (&apiserv->reserve, lsa); + if (!new) + { + /* 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 ("ospf_apiserver_lsa_refresher: Cannot create a new LSA?"); + goto out; + } + } + else + { + /* This is a forcible refresh, requested by OSPF-API client. */ + 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 (new->oi, new) == NULL) + { + zlog_warn ("ospf_apiserver_lsa_refresher: ospf_lsa_install failed"); + ospf_lsa_free (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_info ("LSA[Type%d:%s]: Refresh Opaque LSA", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + +out: + return; +} + + +/* ----------------------------------------------------------- + * Followings 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 in_addr id; + int lsa_type, opaque_type; + int rc = 0; + + /* 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: + case OSPF_OPAQUE_AREA_LSA: + area = ospf_area_lookup_by_area_id (dmsg->area_id); + if (!area) + { + zlog_warn ("ospf_apiserver_lsa_delete: unknown area %s", + inet_ntoa (dmsg->area_id)); + 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 + ("ospf_apiserver_lsa_delete: Cannot delete non-opaque LSA type %d", + 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 ("ospf_apiserver_lsa_delete: LSA-type(%d)/Opaque-type(%d): Not registered", 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 (area, dmsg->lsa_type, id, ospf_top->router_id); + if (!old) + { + zlog_warn ("ospf_apiserver_lsa_delete: LSA[Type%d:%s] not in LSDB", + dmsg->lsa_type, inet_ntoa (id)); + rc = OSPF_API_NOSUCHLSA; + goto out; + } + + /* 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 */ +int +apiserver_flush_opaque_type_callback (struct ospf_lsa *lsa, + void *p_arg, int int_arg) +{ + struct param_t + { + struct ospf_apiserver *apiserv; + u_char lsa_type; + u_char 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, + u_char lsa_type, u_char opaque_type) +{ + struct param_t + { + struct ospf_apiserver *apiserv; + u_char lsa_type; + u_char opaque_type; + } + param; + listnode node; + + /* Set parameter struct. */ + param.apiserv = apiserv; + param.lsa_type = lsa_type; + param.opaque_type = opaque_type; + +#ifdef ORIGINAL_CODING + /* Iterate over all areas */ + for (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = node->data; + + foreach_lsa (OPAQUE_LINK_LSDB (area), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + foreach_lsa (OPAQUE_AREA_LSDB (area), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + } + + /* For AS-external opaque LSAs */ + if (ospf_top->lsdb) + { + foreach_lsa (OPAQUE_AS_LSDB (ospf_top), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + } +#else /* ORIGINAL_CODING */ + switch (lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + for (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = node->data; + foreach_lsa (OPAQUE_LINK_LSDB (area), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + } + break; + case OSPF_OPAQUE_AREA_LSA: + for (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = node->data; + foreach_lsa (OPAQUE_AREA_LSDB (area), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + } + break; + case OSPF_OPAQUE_AS_LSA: + foreach_lsa (OPAQUE_AS_LSDB (ospf_top), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + break; + default: + break; + } + return; +#endif /* ORIGINAL_CODING */ +} + + +/* ----------------------------------------------------------- + * Followings 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 ("ospf_apiserver_new_if: interface has no name?"); + 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 ("ospf_apiserver_new_if: interface has no name?"); + 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 ("ospf_apiserver_new_if: interface %s not known to OSPFd?", + 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 ("ospf_apiserver_del_if"); + zlog_warn ("ifp name=%s status=%d index=%d\n", ifp->name, ifp->status, + ifp->ifindex); + + oi = ospf_apiserver_if_lookup_by_ifp (ifp); + assert (oi); + + /* 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 ("ospf_apiserver_ism_change"); + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_ism_change (oi); + } + + zlog_warn ("oi->ifp->name=%s", oi->ifp->name); + zlog_warn ("old_state=%d", old_state); + zlog_warn ("oi->state=%d", oi->state); +} + +void +ospf_apiserver_nsm_change (struct ospf_neighbor *nbr, int old_status) +{ + /* Neighbor status changed, tell clients about it */ + zlog_warn ("ospf_apiserver_nsm_change"); + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_nsm_change (nbr); + } +} + +void +ospf_apiserver_show_info (struct vty *vty, struct ospf_lsa *lsa) +{ + struct opaque_lsa + { + struct lsa_header header; + u_char 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 (vty != NULL) + { + int i; + vty_out (vty, " Added using OSPF API: %u octets of opaque data %s%s", + opaquelen, + VALID_OPAQUE_INFO_LEN (lsa->data) ? "" : "(Invalid length?)", + VTY_NEWLINE); + vty_out (vty, " Opaque data: "); + + for (i = 0; i < opaquelen; i++) + { + vty_out (vty, "0x%x ", olsa->data[i]); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + else + { + int i; + zlog_info (" Added using OSPF API: %u octets of opaque data %s", + opaquelen, + VALID_OPAQUE_INFO_LEN (lsa-> + data) ? "" : "(Invalid length?)"); + zlog_info (" Opaque data: "); + + for (i = 0; i < opaquelen; i++) + { + zlog_info ("0x%x ", olsa->data[i]); + } + zlog_info ("\n"); + } + return; +} + +/* ----------------------------------------------------------- + * Followings 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) +{ + listnode node; + + /* Send message to all clients */ + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + + 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) +{ + listnode node; + struct msg *msg; + + 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 (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + listnode n2; + + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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 + ("ospf_apiserver_clients_notify_ready_type9: new_msg_ready_notify failed"); +#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) +{ + listnode node; + struct msg *msg; + + assert (area); + + if (!ospf_apiserver_is_ready_type10 (area)) + { + zlog_warn ("Area not ready for type 10?"); + return; + } + + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + listnode n2; + + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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 + ("ospf_apiserver_clients_notify_ready_type10: new_msg_ready_nofity failed"); +#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) +{ + listnode node; + struct msg *msg; + struct in_addr id_null = { 0L }; + + assert (top); + + if (!ospf_apiserver_is_ready_type11 (top)) + { + zlog_warn ("AS not ready for type 11?"); + return; + } + + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + listnode n2; + + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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 + ("ospf_apiserver_clients_notify_ready_type11: new_msg_ready_notify failed"); +#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 = { 0L }; + struct in_addr area_id = { 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->ifp->status); + if (!msg) + { + zlog_warn ("apiserver_clients_notify_ism_change: msg_new failed"); + 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 = { 0L }; + struct in_addr nbraddr = { 0L }; + + assert (nbr); + + if (nbr->oi) + { + 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 ("apiserver_clients_notify_nsm_change: msg_new failed"); + return; + } + + ospf_apiserver_clients_notify_all (msg); + msg_free (msg); +} + +void +apiserver_clients_lsa_change_notify (u_char msgtype, struct ospf_lsa *lsa) +{ + struct msg *msg; + listnode node; + + /* Default area for AS-External and Opaque11 LSAs */ + struct in_addr area_id = { 0L }; + + /* Default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { 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 ("apiserver_clients_lsa_change_notify: msg_new failed"); + return; + } + + /* Now send message to all clients with a matching filter */ + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = (struct ospf_apiserver *) node->data; + struct lsa_filter_type *filter; + u_int16_t mask; + u_int32_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 = (u_int32_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); +} + + +/* ------------------------------------------------------------- + * Followings are hooks invoked when LSAs are updated or deleted + * ------------------------------------------------------------- + */ + + +int +apiserver_notify_clients_lsa (u_char msgtype, struct ospf_lsa *lsa) +{ + struct msg *msg; + /* default area for AS-External and Opaque11 LSAs */ + struct in_addr area_id = { 0L }; + + /* default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { 0L }; + + /* Only notify this update if the LSA's age is smaller than + MAXAGE. Otherwise clients would see LSA updates with max age just + before they are deleted from the LSDB. LSA delete messages have + MAXAGE too but should not be filtered. */ + if (IS_LSA_MAXAGE(lsa) && (msgtype == MSG_LSA_UPDATE_NOTIFY)) { + return 0; + } + + 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 (msgtype, 0L, /* no sequence number */ + ifaddr, area_id, + lsa->flags & OSPF_LSA_SELF, lsa->data); + if (!msg) + { + zlog_warn ("notify_clients_lsa: msg_new failed"); + return -1; + } + /* Notify all clients that new LSA is added/updated */ + apiserver_clients_lsa_change_notify (msgtype, lsa); + + /* Clients made their own copies of msg so we can free msg here */ + msg_free (msg); + + return 0; +} + +int +ospf_apiserver_lsa_update (struct ospf_lsa *lsa) +{ + return apiserver_notify_clients_lsa (MSG_LSA_UPDATE_NOTIFY, lsa); +} + +int +ospf_apiserver_lsa_delete (struct ospf_lsa *lsa) +{ + return apiserver_notify_clients_lsa (MSG_LSA_DELETE_NOTIFY, lsa); +} + +#endif /* SUPPORT_OSPF_API */ + diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h new file mode 100644 index 00000000..c7145782 --- /dev/null +++ b/ospfd/ospf_apiserver.h @@ -0,0 +1,201 @@ +/* + * Server side of OSPF API. + * Copyright (C) 2001, 2002 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _OSPF_APISERVER_H +#define _OSPF_APISERVER_H + +/* MTYPE definition is not reflected to "memory.h". */ +#define MTYPE_OSPF_APISERVER MTYPE_TMP +#define MTYPE_OSPF_APISERVER_MSGFILTER MTYPE_TMP + +/* List of opaque types that application registered */ +struct registered_opaque_type +{ + u_char lsa_type; + u_char opaque_type; +}; + + +/* Server instance for each accepted client connection. */ +struct ospf_apiserver +{ + /* Socket connections for synchronous commands and asynchronous + notifications */ + int fd_sync; /* synchronous requests */ + struct sockaddr_in peer_sync; + + int fd_async; /* asynchronous notifications */ + struct sockaddr_in peer_async; + + /* List of all opaque types that application registers to use. Using + a single connection with the OSPF daemon, multiple + <lsa,opaque_type> pairs can be registered. However, each + combination can only be registered once by all applications. */ + list opaque_types; /* of type registered_opaque_type */ + + /* Temporary storage for LSA instances to be refreshed. */ + struct ospf_lsdb reserve; + + /* filter for LSA update/delete notifies */ + struct lsa_filter_type *filter; + + /* Fifo buffers for outgoing messages */ + struct msg_fifo *out_sync_fifo; + struct msg_fifo *out_async_fifo; + + /* Read and write threads */ + struct thread *t_sync_read; +#ifdef USE_ASYNC_READ + struct thread *t_async_read; +#endif /* USE_ASYNC_READ */ + struct thread *t_sync_write; + struct thread *t_async_write; +}; + +enum event +{ + OSPF_APISERVER_ACCEPT, + OSPF_APISERVER_SYNC_READ, +#ifdef USE_ASYNC_READ + OSPF_APISERVER_ASYNC_READ, +#endif /* USE_ASYNC_READ */ + OSPF_APISERVER_SYNC_WRITE, + OSPF_APISERVER_ASYNC_WRITE +}; + +/* ----------------------------------------------------------- + * Followings are functions to manage client connections. + * ----------------------------------------------------------- + */ + +unsigned short ospf_apiserver_getport (void); +int ospf_apiserver_init (void); +void ospf_apiserver_term (void); +struct ospf_apiserver *ospf_apiserver_new (int fd_sync, int fd_async); +void ospf_apiserver_free (struct ospf_apiserver *apiserv); +void ospf_apiserver_event (enum event event, int fd, + struct ospf_apiserver *apiserv); +int ospf_apiserver_serv_sock_family (unsigned short port, int family); +int ospf_apiserver_accept (struct thread *thread); +int ospf_apiserver_read (struct thread *thread); +int ospf_apiserver_sync_write (struct thread *thread); +int ospf_apiserver_async_write (struct thread *thread); +int ospf_apiserver_send_reply (struct ospf_apiserver *apiserv, + u_int32_t seqnr, u_char rc); + +/* ----------------------------------------------------------- + * Followings are message handler functions + * ----------------------------------------------------------- + */ + +int ospf_apiserver_lsa9_originator (void *arg); +int ospf_apiserver_lsa10_originator (void *arg); +int ospf_apiserver_lsa11_originator (void *arg); + +void ospf_apiserver_clients_notify_all (struct msg *msg); + +void ospf_apiserver_clients_notify_ready_type9 (struct ospf_interface *oi); +void ospf_apiserver_clients_notify_ready_type10 (struct ospf_area *area); +void ospf_apiserver_clients_notify_ready_type11 (struct ospf *top); + +void ospf_apiserver_clients_notify_new_if (struct ospf_interface *oi); +void ospf_apiserver_clients_notify_del_if (struct ospf_interface *oi); +void ospf_apiserver_clients_notify_ism_change (struct ospf_interface *oi); +void ospf_apiserver_clients_notify_nsm_change (struct ospf_neighbor *nbr); + +int ospf_apiserver_is_ready_type9 (struct ospf_interface *oi); +int ospf_apiserver_is_ready_type10 (struct ospf_area *area); +int ospf_apiserver_is_ready_type11 (struct ospf *ospf); + +void ospf_apiserver_notify_ready_type9 (struct ospf_apiserver *apiserv); +void ospf_apiserver_notify_ready_type10 (struct ospf_apiserver *apiserv); +void ospf_apiserver_notify_ready_type11 (struct ospf_apiserver *apiserv); + +int ospf_apiserver_handle_msg (struct ospf_apiserver *apiserv, + struct msg *msg); +int ospf_apiserver_handle_register_opaque_type (struct ospf_apiserver + *apiserv, struct msg *msg); +int ospf_apiserver_handle_unregister_opaque_type (struct ospf_apiserver + *apiserv, struct msg *msg); +int ospf_apiserver_handle_register_event (struct ospf_apiserver *apiserv, + struct msg *msg); +int ospf_apiserver_handle_originate_request (struct ospf_apiserver *apiserv, + struct msg *msg); +int ospf_apiserver_handle_delete_request (struct ospf_apiserver *apiserv, + struct msg *msg); +int ospf_apiserver_handle_sync_lsdb (struct ospf_apiserver *apiserv, + struct msg *msg); + + +/* ----------------------------------------------------------- + * Followings are functions for LSA origination/deletion + * ----------------------------------------------------------- + */ + +int ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserver, + u_char lsa_type, u_char opaque_type); +int ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserver, + u_char lsa_type, + u_char opaque_type); +struct ospf_lsa *ospf_apiserver_opaque_lsa_new (struct ospf_area *area, + struct ospf_interface *oi, + struct lsa_header *protolsa); +struct ospf_interface *ospf_apiserver_if_lookup_by_addr (struct in_addr + address); +struct ospf_interface *ospf_apiserver_if_lookup_by_ifp (struct interface + *ifp); +int ospf_apiserver_originate1 (struct ospf_lsa *lsa); +void ospf_apiserver_flood_opaque_lsa (struct ospf_lsa *lsa); + + +/* ----------------------------------------------------------- + * Followings are callback functions to handle opaque types + * ----------------------------------------------------------- + */ + +int ospf_apiserver_new_if (struct interface *ifp); +int ospf_apiserver_del_if (struct interface *ifp); +void ospf_apiserver_ism_change (struct ospf_interface *oi, int old_status); +void ospf_apiserver_nsm_change (struct ospf_neighbor *nbr, int old_status); +void ospf_apiserver_config_write_router (struct vty *vty); +void ospf_apiserver_config_write_if (struct vty *vty, struct interface *ifp); +void ospf_apiserver_show_info (struct vty *vty, struct ospf_lsa *lsa); +int ospf_ospf_apiserver_lsa_originator (void *arg); +void ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa); +void ospf_apiserver_flush_opaque_lsa (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type); + +/* ----------------------------------------------------------- + * Followings are hooks when LSAs are updated or deleted + * ----------------------------------------------------------- + */ + + +/* Hooks that are invoked from ospf opaque module */ + +int ospf_apiserver_lsa_update (struct ospf_lsa *lsa); +int ospf_apiserver_lsa_delete (struct ospf_lsa *lsa); + +void ospf_apiserver_clients_lsa_change_notify (u_char msgtype, + struct ospf_lsa *lsa); + +#endif /* _OSPF_APISERVER_H */ |