Mailing List Archive

[PATCH 07/23] lib: add basic capn config access protocol
From: David Lamparter <equinox@opensourcerouting.org>

This commit introduces the qzc.capnp schema file. This schema file is
only here for information, despite it is possible to automate c files
generation, from this scheme file.
In addition, qzc framework needs some server API to handle qzc queries
from any qzc client. Among the APIs, there is the possibility to handle
delete/destroy/unset operations. It is also possible to answer to get
operations that request some context to be retrieved. Finally, it is
possible to handle iterations over a table, for example.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
---
lib/.gitignore | 2 +
lib/Makefile.am | 10 +-
lib/memtypes.c | 1 +
lib/qzc.c | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/qzc.capnp | 148 ++++++++++++++++++++++
lib/qzc.h | 95 +++++++++++++++
6 files changed, 626 insertions(+), 2 deletions(-)
create mode 100644 lib/qzc.c
create mode 100644 lib/qzc.capnp
create mode 100644 lib/qzc.h

diff --git a/lib/.gitignore b/lib/.gitignore
index 02aa432ce16d..f30e20b93802 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -3,6 +3,8 @@ Makefile.in
*.o
*.lo
*.la
+*.capnp.c
+*.capnp.h
version.c
version.h
gitversion.h
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 7b6c34dcabb5..c2868333334e 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -36,11 +36,17 @@ noinst_HEADERS = \
plist_int.h

if HAVE_ZEROMQ
-libzebra_la_SOURCES += qzmq.c
-pkginclude_HEADERS += qzmq.h
+libzebra_la_SOURCES += qzmq.c qzc.c
+pkginclude_HEADERS += qzmq.h qzc.h
+endif
+
+if HAVE_CCAPNPROTO
+libzebra_la_SOURCES += qzc.capnp.c
+BUILT_SOURCES += qzc.capnp.c
endif

EXTRA_DIST = \
+ qzc.capnp \
regex.c regex-gnu.h \
queue.h \
memtypes.awk \
diff --git a/lib/memtypes.c b/lib/memtypes.c
index bfda5eb7b14e..3e43a857322e 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -75,6 +75,7 @@ struct memory_list memory_list_lib[] =
{ MTYPE_IF_LINK_PARAMS, "Informational Link Parameters" },
{ MTYPE_LIB_NEXTHOP, "Nexthop" },
{ MTYPE_ZEROMQ_CB, "ZEROMQ_CB" },
+ { MTYPE_QZC_SOCK, "QZC" },
{ -1, NULL },
};

diff --git a/lib/qzc.c b/lib/qzc.c
new file mode 100644
index 000000000000..358b7e419eef
--- /dev/null
+++ b/lib/qzc.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2015-16 David Lamparter, for NetDEF, Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga 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.
+ *
+ * Quagga 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 Quagga; 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>
+
+#include "qzmq.h"
+#include "thread.h"
+#include "memory.h"
+#include "hash.h"
+#include "log.h"
+
+#include "qzc.h"
+
+#include "qzc.capnp.h"
+
+static struct qzc_wkn *wkn_first = NULL;
+
+void qzc_wkn_reg(struct qzc_wkn *wkn)
+{
+ wkn->next = wkn_first;
+ wkn_first = wkn;
+}
+
+static struct qzc_wkn *qzc_wkn_find(uint64_t wid)
+{
+ struct qzc_wkn *wkn;
+ for (wkn = wkn_first; wkn; wkn = wkn->next)
+ if (wkn->wid == wid)
+ return wkn;
+ return NULL;
+}
+
+static struct hash *nodes = NULL;
+
+static unsigned int qzc_key (void *data)
+{
+ struct qzc_node *node = data;
+ return (unsigned int)node->nid;
+}
+
+static int qzc_cmp (const void *a, const void *b)
+{
+ const struct qzc_node *na = a, *nb = b;
+ return na->nid == nb->nid;
+}
+
+void qzc_node_reg(struct qzc_node *node, struct qzc_nodetype *type)
+{
+ node->type = type;
+ do
+ {
+ node->nid = (uint64_t)random();
+ node->nid ^= (uint64_t)random() << 32;
+ }
+ while (hash_get (nodes, node, hash_alloc_intern) != node);
+}
+
+void qzc_node_unreg(struct qzc_node *node)
+{
+ hash_release (nodes, node);
+}
+
+static struct qzc_node *qzc_node_get(uint64_t id)
+{
+ struct qzc_node dummy = { .nid = id };
+ return hash_lookup (nodes, &dummy);
+}
+
+static void qzc_wknresolve (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCWKNResolveReq wknreq;
+ struct QZCWKNResolveRep wknrep;
+ memset(&wknrep, 0, sizeof(wknrep));
+
+ read_QZCWKNResolveReq(&wknreq, req->wknresolve);
+
+ struct qzc_wkn *wkn = qzc_wkn_find(wknreq.wid);
+ wknrep.wid = wknreq.wid;
+ wknrep.nid = wkn ? wkn->resolve() : 0;
+ rep->error = !wkn;
+
+ rep->which = QZCReply_wknresolve;
+ rep->wknresolve = new_QZCWKNResolveRep(cs);
+ write_QZCWKNResolveRep(&wknrep, rep->wknresolve);
+}
+
+static void qzc_nodeinforeq (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCNodeInfoReq nireq;
+ struct QZCNodeInfoRep nirep;
+ struct qzc_node *node;
+ memset(&nirep, 0, sizeof(nirep));
+
+ read_QZCNodeInfoReq(&nireq, req->nodeinforeq);
+ node = qzc_node_get(nireq.nid);
+
+ rep->error = !node;
+ nirep.nid = nireq.nid;
+ nirep.tid = node ? node->type->tid : 0;
+
+ rep->which = QZCReply_nodeinforep;
+ rep->nodeinforep = new_QZCNodeInfoRep(cs);
+ write_QZCNodeInfoRep(&nirep, rep->nodeinforep);
+}
+
+static void qzc_get (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCGetReq greq;
+ struct QZCGetRep grep;
+ struct qzc_node *node;
+ memset(&grep, 0, sizeof(grep));
+
+ read_QZCGetReq(&greq, req->get);
+ node = qzc_node_get(greq.nid);
+
+ rep->which = QZCReply_get;
+ rep->get = new_QZCGetRep(cs);
+
+ memset(&grep, 0, sizeof(grep));
+ grep.nid = greq.nid;
+ grep.elem = greq.elem;
+
+ if (!node || !node->type || !node->type->get)
+ {
+ rep->error = 1;
+ goto out;
+ }
+
+ rep->error = 0;
+
+ void *entity = ((char *)node) - node->type->node_member_offset;
+ node->type->get(entity,
+ &greq, &grep, cs);
+
+out:
+ write_QZCGetRep(&grep, rep->get);
+}
+
+static void qzc_create (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCCreateReq creq;
+ struct QZCCreateRep crep;
+ struct qzc_node *node;
+ memset(&crep, 0, sizeof(crep));
+
+ rep->error = 1;
+
+ read_QZCCreateReq(&creq, req->create);
+ node = qzc_node_get(creq.parentnid);
+
+ rep->which = QZCReply_create;
+ rep->create = new_QZCCreateRep(cs);
+
+ if (!node || !node->type || !node->type->createchild)
+ goto out;
+
+ rep->error = 0;
+
+ void *entity = ((char *)node) - node->type->node_member_offset;
+ node->type->createchild(entity, &creq, &crep, cs);
+
+out:
+ write_QZCCreateRep(&crep, rep->create);
+}
+
+static void qzc_set (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs, bool unset)
+{
+ struct QZCSetReq sreq;
+ struct qzc_node *node;
+
+ read_QZCSetReq(&sreq, req->set);
+ node = qzc_node_get(sreq.nid);
+
+ rep->which = QZCReply_set;
+
+ if (!node || !node->type || !(unset ? node->type->unset : node->type->set))
+ {
+ rep->error = 1;
+ return;
+ }
+
+ rep->error = 0;
+
+ void *entity = ((char *)node) - node->type->node_member_offset;
+ if (unset)
+ node->type->unset(entity, &sreq, cs);
+ else
+ node->type->set(entity, &sreq, cs);
+}
+
+static void qzc_del (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCDelReq dreq;
+ struct qzc_node *node;
+
+ read_QZCDelReq(&dreq, req->del);
+ node = qzc_node_get(dreq.nid);
+
+ rep->which = QZCReply_del;
+
+ if (!node || !node->type || !node->type->destroy)
+ {
+ rep->error = 1;
+ return;
+ }
+
+ rep->error = 0;
+
+ void *entity = ((char *)node) - node->type->node_member_offset;
+ node->type->destroy(entity, &dreq, cs);
+}
+
+
+static void qzc_callback (void *arg, void *zmqsock, zmq_msg_t *msg)
+{
+ int64_t more = 0;
+ size_t more_size;
+ int ret;
+
+ void *data = zmq_msg_data (msg);
+ size_t size = zmq_msg_size (msg);
+
+ struct capn ctx;
+ capn_init_mem(&ctx, data, size, 0);
+
+ struct QZCRequest req;
+ struct QZCReply rep;
+
+ QZCRequest_ptr root;
+ root.p = capn_getp(capn_root(&ctx), 0, 1);
+ read_QZCRequest(&req, root);
+
+ struct capn rc;
+ capn_init_malloc(&rc);
+ struct capn_segment *cs = capn_root(&rc).seg;
+
+ rep.error = 0;
+ switch (req.which)
+ {
+ case QZCRequest_ping:
+ rep.which = QZCReply_pong;
+ break;
+ case QZCRequest_wknresolve:
+ qzc_wknresolve(&req, &rep, cs);
+ break;
+ case QZCRequest_nodeinforeq:
+ qzc_nodeinforeq(&req, &rep, cs);
+ break;
+ case QZCRequest_get:
+ qzc_get(&req, &rep, cs);
+ break;
+ case QZCRequest_create:
+ qzc_create(&req, &rep, cs);
+ break;
+ case QZCRequest_set:
+ qzc_set(&req, &rep, cs, false);
+ break;
+ case QZCRequest_unset:
+ qzc_set(&req, &rep, cs, true);
+ break;
+ case QZCRequest_del:
+ qzc_del(&req, &rep, cs);
+ break;
+ };
+
+ QZCReply_ptr rp = new_QZCReply(cs);
+ write_QZCReply(&rep, rp);
+ capn_setp(capn_root(&rc), 0, rp.p);
+
+ uint8_t buf[4096];
+ ssize_t rs = capn_write_mem(&rc, buf, sizeof(buf), 0);
+ capn_free(&ctx);
+ capn_free(&rc);
+
+ zlog_info ("QZC request type %d, response type %d, %zd bytes, error=%d", req.which, rep.which, rs, rep.error);
+ zmq_send (zmqsock, buf, rs, 0);
+
+ do
+ {
+ more_size = sizeof (more);
+ ret = zmq_getsockopt (zmqsock, ZMQ_RCVMORE, &more, &more_size);
+
+ if (!more)
+ break;
+
+ ret = zmq_msg_recv (msg, zmqsock, ZMQ_NOBLOCK);
+ if (ret < 0)
+ {
+ zlog_err ("zmq_msg_recv failed: %s (%d)", strerror (errno), errno);
+ break;
+ }
+ }
+ while (1);
+}
+
+void qzc_init (void)
+{
+ qzmq_init ();
+ if(!nodes)
+ nodes = hash_create (qzc_key, qzc_cmp);
+}
+
+void qzc_finish (void)
+{
+ hash_free (nodes);
+ nodes = NULL;
+
+ qzmq_finish();
+}
+
+struct qzc_sock {
+ void *zmq;
+ struct qzmq_cb *cb;
+};
+
+struct qzc_sock *qzc_bind (struct thread_master *master, const char *url)
+{
+ void *qzc_sock;
+ struct qzc_sock *ret;
+
+ qzc_sock = zmq_socket (qzmq_context, ZMQ_REP);
+
+ if (!qzc_sock)
+ {
+ zlog_err ("zmq_socket failed: %s (%d)", strerror (errno), errno);
+ return NULL;
+ }
+
+ if (zmq_bind (qzc_sock, url))
+ {
+ zlog_err ("zmq_bind failed: %s (%d)", strerror (errno), errno);
+ zmq_close (qzc_sock);
+ return NULL;
+ }
+
+ ret = XCALLOC(MTYPE_QZC_SOCK, sizeof(*ret));
+ ret->zmq = qzc_sock;
+ ret->cb = qzmq_thread_read_msg (master, qzc_callback, NULL, qzc_sock);
+ return ret;
+}
+
+void qzc_close (struct qzc_sock *sock)
+{
+ if (sock->cb)
+ qzmq_thread_cancel (sock->cb);
+ zmq_close (sock->zmq);
+ XFREE(MTYPE_QZC_SOCK, sock);
+}
diff --git a/lib/qzc.capnp b/lib/qzc.capnp
new file mode 100644
index 000000000000..e6e58ea4d445
--- /dev/null
+++ b/lib/qzc.capnp
@@ -0,0 +1,148 @@
+#
+# Copyright (c) 2016 David Lamparter, for NetDEF, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+@0xc7bbe66a583460d4;
+
+# types of IDs:
+# - wkn: wellknown. can be resolved to a nid, but may not always exist.
+# also, the nid can change if an object is destroyed and
+# recreated.
+#
+# - nid: node id. does not change over the lifetime of an object, but
+# will change over restarts. will also changed when some
+# object is destroyed & recreated.
+#
+# - elem: element. a data item on a node. for example, config & status
+# might be 2 data elements. children node lists are also
+# separate elements (to make walking easier).
+#
+# note: elements can have
+#
+# - tid: type id. constant type id of a node's type.
+#
+# - datatype, references to Cap'n'Proto struct IDs; specifies what
+# ctxtype: kind of data is carried
+#
+
+# NodeInfo is mostly useless, but a very simple operation
+#
+struct QZCNodeInfoReq {
+ nid @0 :UInt64;
+}
+
+struct QZCNodeInfoRep {
+ nid @0 :UInt64;
+ tid @1 :UInt64;
+}
+
+# resolve a well-known ID to a node id. first operation on client really.
+#
+struct QZCWKNResolveReq {
+ wid @0 :UInt64;
+}
+
+struct QZCWKNResolveRep {
+ wid @0 :UInt64;
+ nid @1 :UInt64;
+}
+
+# get data for a specific node element
+#
+struct QZCGetReq {
+ nid @0 :UInt64;
+ elem @1 :UInt64;
+ ctxtype @2 :UInt64;
+ ctxdata @3 :AnyPointer;
+ itertype @4 :UInt64;
+ iterdata @5 :AnyPointer;
+}
+
+struct QZCGetRep {
+ nid @0 :UInt64;
+ elem @1 :UInt64;
+ datatype @2 :UInt64;
+ data @3 :AnyPointer;
+ itertype @4 :UInt64;
+ nextiter @5 :AnyPointer;
+}
+
+# create child in context of a parent node/element
+#
+struct QZCCreateReq {
+ parentnid @0 :UInt64;
+ parentelem @1 :UInt64;
+ datatype @2 :UInt64;
+ data @3 :AnyPointer;
+}
+
+struct QZCCreateRep {
+ newnid @0 :UInt64;
+}
+
+struct QZCSetReq {
+ nid @0 :UInt64;
+ elem @1 :UInt64;
+ ctxtype @2 :UInt64;
+ ctxdata @3 :AnyPointer;
+ datatype @4 :UInt64;
+ data @5 :AnyPointer;
+}
+
+struct QZCDelReq {
+ nid @0 :UInt64;
+}
+
+struct QZCRequest {
+ union {
+ ping @0 :Void;
+ nodeinforeq @1 :QZCNodeInfoReq;
+ wknresolve @2 :QZCWKNResolveReq;
+ get @3 :QZCGetReq;
+ create @4 :QZCCreateReq;
+ set @5 :QZCSetReq;
+ del @6 :QZCDelReq;
+ unset @7 :QZCSetReq;
+ }
+}
+
+# TBD: better error handling *cough*
+struct QZCReply {
+ error @0 :Bool;
+ union {
+ pong @1 :Void;
+ nodeinforep @2 :QZCNodeInfoRep;
+ wknresolve @3 :QZCWKNResolveRep;
+ get @4 :QZCGetRep;
+ create @5 :QZCCreateRep;
+ set @6 :Void;
+ del @7 :Void;
+ unset @8 :Void;
+ }
+}
+
+# datatype / data of "nodelist" elements.
+# TBD: windowed iterator? hopefully not needed...
+#
+struct QZCNodeList @0x9bb91c45a95a581d {
+ nodes @0 :List(UInt64);
+}
+
diff --git a/lib/qzc.h b/lib/qzc.h
new file mode 100644
index 000000000000..b5326dd8c314
--- /dev/null
+++ b/lib/qzc.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015-16 David Lamparter, for NetDEF, Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga 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.
+ *
+ * Quagga 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 Quagga; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _QZC_H
+#define _QZC_H
+#ifdef HAVE_CCAPNPROTO
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include "qzc.capnp.h"
+
+struct qzc_nodetype {
+ uint64_t tid;
+ ptrdiff_t node_member_offset;
+ void (*get)(void *entity, struct QZCGetReq *req, struct QZCGetRep *rep,
+ struct capn_segment *seg);
+ void (*createchild)(void *parent, struct QZCCreateReq *req,
+ struct QZCCreateRep *rep, struct capn_segment *seg);
+ void (*set)(void *entity, struct QZCSetReq *req,
+ struct capn_segment *seg);
+ void (*unset)(void *entity, struct QZCSetReq *req,
+ struct capn_segment *seg);
+ void (*destroy)(void *entity, struct QZCDelReq *req,
+ struct capn_segment *seg);
+};
+
+struct qzc_node {
+ uint64_t nid;
+ struct qzc_nodetype *type;
+};
+
+#define QZC_NODE \
+ struct qzc_node qzc_node;
+
+#define QZC_NODE_REG(n, structname) \
+ qzc_node_reg(&n->qzc_node, &qzc_t_ ## structname);
+#define QZC_NODE_UNREG(n) \
+ qzc_node_unreg(&n->qzc_node);
+
+void qzc_node_reg(struct qzc_node *node, struct qzc_nodetype *type);
+void qzc_node_unreg(struct qzc_node *node);
+
+#define EXT_QZC_NODETYPE(structname) \
+ extern struct qzc_nodetype qzc_t_ ## structname;
+#define QZC_NODETYPE(structname, id) \
+ struct qzc_nodetype qzc_t_ ## structname = { \
+ .tid = id, \
+ .node_member_offset = \
+ (ptrdiff_t)offsetof(struct structname, qzc_node) \
+ };
+void qzc_nodetype_init(struct qzc_nodetype *type);
+
+struct qzc_sock;
+
+void qzc_init(void);
+void qzc_finish(void);
+struct qzc_sock *qzc_bind(struct thread_master *master, const char *url);
+void qzc_close(struct qzc_sock *sock);
+
+struct qzc_wkn {
+ uint64_t wid;
+ uint64_t (*resolve)(void);
+
+ struct qzc_wkn *next;
+};
+void qzc_wkn_reg(struct qzc_wkn *wkn);
+#else
+
+#define QZC_NODE
+#define QZC_NODE_REG(n, structname)
+#define QZC_NODE_UNREG(n)
+#define EXT_QZC_NODETYPE(structname)
+#define QZC_NODETYPE(structname, id)
+
+#endif /* HAVE_CCAPNPROTO */
+#endif /* _QZC_H */
--
2.1.4


_______________________________________________
Quagga-dev mailing list
Quagga-dev@lists.quagga.net
https://lists.quagga.net/mailman/listinfo/quagga-dev
[PATCH 07/23] lib: add basic capn config access protocol [ In reply to ]
From: David Lamparter <equinox@opensourcerouting.org>

This commit introduces the qzc.capnp schema file. This schema file is
only here for information, despite it is possible to automate c files
generation, from this scheme file.
In addition, qzc framework needs some server API to handle qzc queries
from any qzc client. Among the APIs, there is the possibility to handle
delete/destroy/unset operations. It is also possible to answer to get
operations that request some context to be retrieved. Finally, it is
possible to handle iterations over a table, for example.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
---
lib/.gitignore | 2 +
lib/Makefile.am | 10 +-
lib/memtypes.c | 1 +
lib/qzc.c | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/qzc.capnp | 148 ++++++++++++++++++++++
lib/qzc.h | 95 +++++++++++++++
6 files changed, 626 insertions(+), 2 deletions(-)
create mode 100644 lib/qzc.c
create mode 100644 lib/qzc.capnp
create mode 100644 lib/qzc.h

diff --git a/lib/.gitignore b/lib/.gitignore
index 02aa432ce16d..f30e20b93802 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -3,6 +3,8 @@ Makefile.in
*.o
*.lo
*.la
+*.capnp.c
+*.capnp.h
version.c
version.h
gitversion.h
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 7b6c34dcabb5..c2868333334e 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -36,11 +36,17 @@ noinst_HEADERS = \
plist_int.h

if HAVE_ZEROMQ
-libzebra_la_SOURCES += qzmq.c
-pkginclude_HEADERS += qzmq.h
+libzebra_la_SOURCES += qzmq.c qzc.c
+pkginclude_HEADERS += qzmq.h qzc.h
+endif
+
+if HAVE_CCAPNPROTO
+libzebra_la_SOURCES += qzc.capnp.c
+BUILT_SOURCES += qzc.capnp.c
endif

EXTRA_DIST = \
+ qzc.capnp \
regex.c regex-gnu.h \
queue.h \
memtypes.awk \
diff --git a/lib/memtypes.c b/lib/memtypes.c
index bfda5eb7b14e..3e43a857322e 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -75,6 +75,7 @@ struct memory_list memory_list_lib[] =
{ MTYPE_IF_LINK_PARAMS, "Informational Link Parameters" },
{ MTYPE_LIB_NEXTHOP, "Nexthop" },
{ MTYPE_ZEROMQ_CB, "ZEROMQ_CB" },
+ { MTYPE_QZC_SOCK, "QZC" },
{ -1, NULL },
};

diff --git a/lib/qzc.c b/lib/qzc.c
new file mode 100644
index 000000000000..358b7e419eef
--- /dev/null
+++ b/lib/qzc.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2015-16 David Lamparter, for NetDEF, Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga 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.
+ *
+ * Quagga 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 Quagga; 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>
+
+#include "qzmq.h"
+#include "thread.h"
+#include "memory.h"
+#include "hash.h"
+#include "log.h"
+
+#include "qzc.h"
+
+#include "qzc.capnp.h"
+
+static struct qzc_wkn *wkn_first = NULL;
+
+void qzc_wkn_reg(struct qzc_wkn *wkn)
+{
+ wkn->next = wkn_first;
+ wkn_first = wkn;
+}
+
+static struct qzc_wkn *qzc_wkn_find(uint64_t wid)
+{
+ struct qzc_wkn *wkn;
+ for (wkn = wkn_first; wkn; wkn = wkn->next)
+ if (wkn->wid == wid)
+ return wkn;
+ return NULL;
+}
+
+static struct hash *nodes = NULL;
+
+static unsigned int qzc_key (void *data)
+{
+ struct qzc_node *node = data;
+ return (unsigned int)node->nid;
+}
+
+static int qzc_cmp (const void *a, const void *b)
+{
+ const struct qzc_node *na = a, *nb = b;
+ return na->nid == nb->nid;
+}
+
+void qzc_node_reg(struct qzc_node *node, struct qzc_nodetype *type)
+{
+ node->type = type;
+ do
+ {
+ node->nid = (uint64_t)random();
+ node->nid ^= (uint64_t)random() << 32;
+ }
+ while (hash_get (nodes, node, hash_alloc_intern) != node);
+}
+
+void qzc_node_unreg(struct qzc_node *node)
+{
+ hash_release (nodes, node);
+}
+
+static struct qzc_node *qzc_node_get(uint64_t id)
+{
+ struct qzc_node dummy = { .nid = id };
+ return hash_lookup (nodes, &dummy);
+}
+
+static void qzc_wknresolve (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCWKNResolveReq wknreq;
+ struct QZCWKNResolveRep wknrep;
+ memset(&wknrep, 0, sizeof(wknrep));
+
+ read_QZCWKNResolveReq(&wknreq, req->wknresolve);
+
+ struct qzc_wkn *wkn = qzc_wkn_find(wknreq.wid);
+ wknrep.wid = wknreq.wid;
+ wknrep.nid = wkn ? wkn->resolve() : 0;
+ rep->error = !wkn;
+
+ rep->which = QZCReply_wknresolve;
+ rep->wknresolve = new_QZCWKNResolveRep(cs);
+ write_QZCWKNResolveRep(&wknrep, rep->wknresolve);
+}
+
+static void qzc_nodeinforeq (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCNodeInfoReq nireq;
+ struct QZCNodeInfoRep nirep;
+ struct qzc_node *node;
+ memset(&nirep, 0, sizeof(nirep));
+
+ read_QZCNodeInfoReq(&nireq, req->nodeinforeq);
+ node = qzc_node_get(nireq.nid);
+
+ rep->error = !node;
+ nirep.nid = nireq.nid;
+ nirep.tid = node ? node->type->tid : 0;
+
+ rep->which = QZCReply_nodeinforep;
+ rep->nodeinforep = new_QZCNodeInfoRep(cs);
+ write_QZCNodeInfoRep(&nirep, rep->nodeinforep);
+}
+
+static void qzc_get (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCGetReq greq;
+ struct QZCGetRep grep;
+ struct qzc_node *node;
+ memset(&grep, 0, sizeof(grep));
+
+ read_QZCGetReq(&greq, req->get);
+ node = qzc_node_get(greq.nid);
+
+ rep->which = QZCReply_get;
+ rep->get = new_QZCGetRep(cs);
+
+ memset(&grep, 0, sizeof(grep));
+ grep.nid = greq.nid;
+ grep.elem = greq.elem;
+
+ if (!node || !node->type || !node->type->get)
+ {
+ rep->error = 1;
+ goto out;
+ }
+
+ rep->error = 0;
+
+ void *entity = ((char *)node) - node->type->node_member_offset;
+ node->type->get(entity,
+ &greq, &grep, cs);
+
+out:
+ write_QZCGetRep(&grep, rep->get);
+}
+
+static void qzc_create (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCCreateReq creq;
+ struct QZCCreateRep crep;
+ struct qzc_node *node;
+ memset(&crep, 0, sizeof(crep));
+
+ rep->error = 1;
+
+ read_QZCCreateReq(&creq, req->create);
+ node = qzc_node_get(creq.parentnid);
+
+ rep->which = QZCReply_create;
+ rep->create = new_QZCCreateRep(cs);
+
+ if (!node || !node->type || !node->type->createchild)
+ goto out;
+
+ rep->error = 0;
+
+ void *entity = ((char *)node) - node->type->node_member_offset;
+ node->type->createchild(entity, &creq, &crep, cs);
+
+out:
+ write_QZCCreateRep(&crep, rep->create);
+}
+
+static void qzc_set (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs, bool unset)
+{
+ struct QZCSetReq sreq;
+ struct qzc_node *node;
+
+ read_QZCSetReq(&sreq, req->set);
+ node = qzc_node_get(sreq.nid);
+
+ rep->which = QZCReply_set;
+
+ if (!node || !node->type || !(unset ? node->type->unset : node->type->set))
+ {
+ rep->error = 1;
+ return;
+ }
+
+ rep->error = 0;
+
+ void *entity = ((char *)node) - node->type->node_member_offset;
+ if (unset)
+ node->type->unset(entity, &sreq, cs);
+ else
+ node->type->set(entity, &sreq, cs);
+}
+
+static void qzc_del (struct QZCRequest *req, struct QZCReply *rep,
+ struct capn_segment *cs)
+{
+ struct QZCDelReq dreq;
+ struct qzc_node *node;
+
+ read_QZCDelReq(&dreq, req->del);
+ node = qzc_node_get(dreq.nid);
+
+ rep->which = QZCReply_del;
+
+ if (!node || !node->type || !node->type->destroy)
+ {
+ rep->error = 1;
+ return;
+ }
+
+ rep->error = 0;
+
+ void *entity = ((char *)node) - node->type->node_member_offset;
+ node->type->destroy(entity, &dreq, cs);
+}
+
+
+static void qzc_callback (void *arg, void *zmqsock, zmq_msg_t *msg)
+{
+ int64_t more = 0;
+ size_t more_size;
+ int ret;
+
+ void *data = zmq_msg_data (msg);
+ size_t size = zmq_msg_size (msg);
+
+ struct capn ctx;
+ capn_init_mem(&ctx, data, size, 0);
+
+ struct QZCRequest req;
+ struct QZCReply rep;
+
+ QZCRequest_ptr root;
+ root.p = capn_getp(capn_root(&ctx), 0, 1);
+ read_QZCRequest(&req, root);
+
+ struct capn rc;
+ capn_init_malloc(&rc);
+ struct capn_segment *cs = capn_root(&rc).seg;
+
+ rep.error = 0;
+ switch (req.which)
+ {
+ case QZCRequest_ping:
+ rep.which = QZCReply_pong;
+ break;
+ case QZCRequest_wknresolve:
+ qzc_wknresolve(&req, &rep, cs);
+ break;
+ case QZCRequest_nodeinforeq:
+ qzc_nodeinforeq(&req, &rep, cs);
+ break;
+ case QZCRequest_get:
+ qzc_get(&req, &rep, cs);
+ break;
+ case QZCRequest_create:
+ qzc_create(&req, &rep, cs);
+ break;
+ case QZCRequest_set:
+ qzc_set(&req, &rep, cs, false);
+ break;
+ case QZCRequest_unset:
+ qzc_set(&req, &rep, cs, true);
+ break;
+ case QZCRequest_del:
+ qzc_del(&req, &rep, cs);
+ break;
+ };
+
+ QZCReply_ptr rp = new_QZCReply(cs);
+ write_QZCReply(&rep, rp);
+ capn_setp(capn_root(&rc), 0, rp.p);
+
+ uint8_t buf[4096];
+ ssize_t rs = capn_write_mem(&rc, buf, sizeof(buf), 0);
+ capn_free(&ctx);
+ capn_free(&rc);
+
+ zlog_info ("QZC request type %d, response type %d, %zd bytes, error=%d", req.which, rep.which, rs, rep.error);
+ zmq_send (zmqsock, buf, rs, 0);
+
+ do
+ {
+ more_size = sizeof (more);
+ ret = zmq_getsockopt (zmqsock, ZMQ_RCVMORE, &more, &more_size);
+
+ if (!more)
+ break;
+
+ ret = zmq_msg_recv (msg, zmqsock, ZMQ_NOBLOCK);
+ if (ret < 0)
+ {
+ zlog_err ("zmq_msg_recv failed: %s (%d)", strerror (errno), errno);
+ break;
+ }
+ }
+ while (1);
+}
+
+void qzc_init (void)
+{
+ qzmq_init ();
+ if(!nodes)
+ nodes = hash_create (qzc_key, qzc_cmp);
+}
+
+void qzc_finish (void)
+{
+ hash_free (nodes);
+ nodes = NULL;
+
+ qzmq_finish();
+}
+
+struct qzc_sock {
+ void *zmq;
+ struct qzmq_cb *cb;
+};
+
+struct qzc_sock *qzc_bind (struct thread_master *master, const char *url)
+{
+ void *qzc_sock;
+ struct qzc_sock *ret;
+
+ qzc_sock = zmq_socket (qzmq_context, ZMQ_REP);
+
+ if (!qzc_sock)
+ {
+ zlog_err ("zmq_socket failed: %s (%d)", strerror (errno), errno);
+ return NULL;
+ }
+
+ if (zmq_bind (qzc_sock, url))
+ {
+ zlog_err ("zmq_bind failed: %s (%d)", strerror (errno), errno);
+ zmq_close (qzc_sock);
+ return NULL;
+ }
+
+ ret = XCALLOC(MTYPE_QZC_SOCK, sizeof(*ret));
+ ret->zmq = qzc_sock;
+ ret->cb = qzmq_thread_read_msg (master, qzc_callback, NULL, qzc_sock);
+ return ret;
+}
+
+void qzc_close (struct qzc_sock *sock)
+{
+ if (sock->cb)
+ qzmq_thread_cancel (sock->cb);
+ zmq_close (sock->zmq);
+ XFREE(MTYPE_QZC_SOCK, sock);
+}
diff --git a/lib/qzc.capnp b/lib/qzc.capnp
new file mode 100644
index 000000000000..e6e58ea4d445
--- /dev/null
+++ b/lib/qzc.capnp
@@ -0,0 +1,148 @@
+#
+# Copyright (c) 2016 David Lamparter, for NetDEF, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+@0xc7bbe66a583460d4;
+
+# types of IDs:
+# - wkn: wellknown. can be resolved to a nid, but may not always exist.
+# also, the nid can change if an object is destroyed and
+# recreated.
+#
+# - nid: node id. does not change over the lifetime of an object, but
+# will change over restarts. will also changed when some
+# object is destroyed & recreated.
+#
+# - elem: element. a data item on a node. for example, config & status
+# might be 2 data elements. children node lists are also
+# separate elements (to make walking easier).
+#
+# note: elements can have
+#
+# - tid: type id. constant type id of a node's type.
+#
+# - datatype, references to Cap'n'Proto struct IDs; specifies what
+# ctxtype: kind of data is carried
+#
+
+# NodeInfo is mostly useless, but a very simple operation
+#
+struct QZCNodeInfoReq {
+ nid @0 :UInt64;
+}
+
+struct QZCNodeInfoRep {
+ nid @0 :UInt64;
+ tid @1 :UInt64;
+}
+
+# resolve a well-known ID to a node id. first operation on client really.
+#
+struct QZCWKNResolveReq {
+ wid @0 :UInt64;
+}
+
+struct QZCWKNResolveRep {
+ wid @0 :UInt64;
+ nid @1 :UInt64;
+}
+
+# get data for a specific node element
+#
+struct QZCGetReq {
+ nid @0 :UInt64;
+ elem @1 :UInt64;
+ ctxtype @2 :UInt64;
+ ctxdata @3 :AnyPointer;
+ itertype @4 :UInt64;
+ iterdata @5 :AnyPointer;
+}
+
+struct QZCGetRep {
+ nid @0 :UInt64;
+ elem @1 :UInt64;
+ datatype @2 :UInt64;
+ data @3 :AnyPointer;
+ itertype @4 :UInt64;
+ nextiter @5 :AnyPointer;
+}
+
+# create child in context of a parent node/element
+#
+struct QZCCreateReq {
+ parentnid @0 :UInt64;
+ parentelem @1 :UInt64;
+ datatype @2 :UInt64;
+ data @3 :AnyPointer;
+}
+
+struct QZCCreateRep {
+ newnid @0 :UInt64;
+}
+
+struct QZCSetReq {
+ nid @0 :UInt64;
+ elem @1 :UInt64;
+ ctxtype @2 :UInt64;
+ ctxdata @3 :AnyPointer;
+ datatype @4 :UInt64;
+ data @5 :AnyPointer;
+}
+
+struct QZCDelReq {
+ nid @0 :UInt64;
+}
+
+struct QZCRequest {
+ union {
+ ping @0 :Void;
+ nodeinforeq @1 :QZCNodeInfoReq;
+ wknresolve @2 :QZCWKNResolveReq;
+ get @3 :QZCGetReq;
+ create @4 :QZCCreateReq;
+ set @5 :QZCSetReq;
+ del @6 :QZCDelReq;
+ unset @7 :QZCSetReq;
+ }
+}
+
+# TBD: better error handling *cough*
+struct QZCReply {
+ error @0 :Bool;
+ union {
+ pong @1 :Void;
+ nodeinforep @2 :QZCNodeInfoRep;
+ wknresolve @3 :QZCWKNResolveRep;
+ get @4 :QZCGetRep;
+ create @5 :QZCCreateRep;
+ set @6 :Void;
+ del @7 :Void;
+ unset @8 :Void;
+ }
+}
+
+# datatype / data of "nodelist" elements.
+# TBD: windowed iterator? hopefully not needed...
+#
+struct QZCNodeList @0x9bb91c45a95a581d {
+ nodes @0 :List(UInt64);
+}
+
diff --git a/lib/qzc.h b/lib/qzc.h
new file mode 100644
index 000000000000..b5326dd8c314
--- /dev/null
+++ b/lib/qzc.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015-16 David Lamparter, for NetDEF, Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga 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.
+ *
+ * Quagga 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 Quagga; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _QZC_H
+#define _QZC_H
+#ifdef HAVE_CCAPNPROTO
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include "qzc.capnp.h"
+
+struct qzc_nodetype {
+ uint64_t tid;
+ ptrdiff_t node_member_offset;
+ void (*get)(void *entity, struct QZCGetReq *req, struct QZCGetRep *rep,
+ struct capn_segment *seg);
+ void (*createchild)(void *parent, struct QZCCreateReq *req,
+ struct QZCCreateRep *rep, struct capn_segment *seg);
+ void (*set)(void *entity, struct QZCSetReq *req,
+ struct capn_segment *seg);
+ void (*unset)(void *entity, struct QZCSetReq *req,
+ struct capn_segment *seg);
+ void (*destroy)(void *entity, struct QZCDelReq *req,
+ struct capn_segment *seg);
+};
+
+struct qzc_node {
+ uint64_t nid;
+ struct qzc_nodetype *type;
+};
+
+#define QZC_NODE \
+ struct qzc_node qzc_node;
+
+#define QZC_NODE_REG(n, structname) \
+ qzc_node_reg(&n->qzc_node, &qzc_t_ ## structname);
+#define QZC_NODE_UNREG(n) \
+ qzc_node_unreg(&n->qzc_node);
+
+void qzc_node_reg(struct qzc_node *node, struct qzc_nodetype *type);
+void qzc_node_unreg(struct qzc_node *node);
+
+#define EXT_QZC_NODETYPE(structname) \
+ extern struct qzc_nodetype qzc_t_ ## structname;
+#define QZC_NODETYPE(structname, id) \
+ struct qzc_nodetype qzc_t_ ## structname = { \
+ .tid = id, \
+ .node_member_offset = \
+ (ptrdiff_t)offsetof(struct structname, qzc_node) \
+ };
+void qzc_nodetype_init(struct qzc_nodetype *type);
+
+struct qzc_sock;
+
+void qzc_init(void);
+void qzc_finish(void);
+struct qzc_sock *qzc_bind(struct thread_master *master, const char *url);
+void qzc_close(struct qzc_sock *sock);
+
+struct qzc_wkn {
+ uint64_t wid;
+ uint64_t (*resolve)(void);
+
+ struct qzc_wkn *next;
+};
+void qzc_wkn_reg(struct qzc_wkn *wkn);
+#else
+
+#define QZC_NODE
+#define QZC_NODE_REG(n, structname)
+#define QZC_NODE_UNREG(n)
+#define EXT_QZC_NODETYPE(structname)
+#define QZC_NODETYPE(structname, id)
+
+#endif /* HAVE_CCAPNPROTO */
+#endif /* _QZC_H */
--
2.1.4


_______________________________________________
Quagga-dev mailing list
Quagga-dev@lists.quagga.net
https://lists.quagga.net/mailman/listinfo/quagga-dev