From 6e005e0f6b550c9b8a49740c9b2ae3caf8c4317f Mon Sep 17 00:00:00 2001
From: Sander Vrijders <sander.vrijders@intec.ugent.be>
Date: Fri, 4 Nov 2016 14:03:34 +0100
Subject: ipcpd: normal: Add RIB objects

This adds the ability to create/update/destroy RIB objects. Syncing
with other DIF members is not yet supported.
---
 src/ipcpd/normal/ribmgr.c | 512 +++++++++++++++++++++++++++++++++++++++++++++-
 src/ipcpd/normal/ribmgr.h |   4 -
 src/ipcpd/normal/ro.h     |  77 +++++++
 3 files changed, 587 insertions(+), 6 deletions(-)
 create mode 100644 src/ipcpd/normal/ro.h

(limited to 'src')

diff --git a/src/ipcpd/normal/ribmgr.c b/src/ipcpd/normal/ribmgr.c
index 60872ef2..96c7e7e0 100644
--- a/src/ipcpd/normal/ribmgr.c
+++ b/src/ipcpd/normal/ribmgr.c
@@ -28,6 +28,8 @@
 #include <ouroboros/list.h>
 #include <ouroboros/time_utils.h>
 #include <ouroboros/ipcp-dev.h>
+#include <ouroboros/bitmap.h>
+#include <ouroboros/errno.h>
 
 #include <stdlib.h>
 #include <pthread.h>
@@ -40,12 +42,35 @@
 #include "frct.h"
 #include "ipcp.h"
 #include "cdap_request.h"
+#include "ro.h"
 
 #include "static_info.pb-c.h"
 typedef StaticInfoMsg static_info_msg_t;
 
-#define ENROLLMENT  "enrollment"
-#define STATIC_INFO "static DIF information"
+#define SUBS_SIZE 25
+
+#define ENROLLMENT     "enrollment"
+#define STATIC_INFO    "static DIF information"
+#define PATH_DELIMITER "/"
+
+/* RIB objects */
+struct rnode {
+        char *            name;
+
+        /*
+         * NOTE: Naive implementation for now, could be replaced by
+         * for instance taking a hash of the pathname and using that
+         * as an index in a B-tree
+         */
+
+        /* If there are no children, this is a leaf */
+        struct rnode *    child;
+        struct rnode *    sibling;
+
+        struct ro_props * props;
+        uint8_t *         data;
+        size_t            len;
+};
 
 struct mgmt_flow {
         struct cdap *    instance;
@@ -53,7 +78,21 @@ struct mgmt_flow {
         struct list_head next;
 };
 
+struct ro_sub {
+        int                 sid;
+        char *              name;
+        struct ro_sub_ops * ops;
+        struct list_head    next;
+};
+
 struct {
+        struct rnode *     root;
+        pthread_mutex_t    ro_lock;
+
+        struct list_head   subs;
+        struct bmp *       sids;
+        pthread_mutex_t    subs_lock;
+
         struct dt_const    dtc;
 
         uint64_t           address;
@@ -116,20 +155,72 @@ int ribmgr_init()
 {
         INIT_LIST_HEAD(&rib.flows);
         INIT_LIST_HEAD(&rib.cdap_reqs);
+        INIT_LIST_HEAD(&rib.subs);
+
+        rib.root = malloc(sizeof(*(rib.root)));
+        if (rib.root == NULL)
+                return -1;
+
+        rib.root->name = "root";
+        rib.root->child = NULL;
+        rib.root->sibling = NULL;
 
         if (pthread_rwlock_init(&rib.flows_lock, NULL)) {
                 LOG_ERR("Failed to initialize rwlock.");
+                free(rib.root);
                 return -1;
         }
 
         if (pthread_mutex_init(&rib.cdap_reqs_lock, NULL)) {
                 LOG_ERR("Failed to initialize mutex.");
+                pthread_rwlock_destroy(&rib.flows_lock);
+                free(rib.root);
+                return -1;
+        }
+
+        if (pthread_mutex_init(&rib.ro_lock, NULL)) {
+                LOG_ERR("Failed to initialize mutex.");
+                pthread_rwlock_destroy(&rib.flows_lock);
+                pthread_mutex_destroy(&rib.cdap_reqs_lock);
+                free(rib.root);
+                return -1;
+        }
+
+        if (pthread_mutex_init(&rib.subs_lock, NULL)) {
+                LOG_ERR("Failed to initialize mutex.");
+                pthread_rwlock_destroy(&rib.flows_lock);
+                pthread_mutex_destroy(&rib.cdap_reqs_lock);
+                pthread_mutex_destroy(&rib.ro_lock);
+                free(rib.root);
+                return -1;
+        }
+
+        rib.sids = bmp_create(SUBS_SIZE, 0);
+        if (rib.sids == NULL) {
+                LOG_ERR("Failed to create bitmap.");
+                pthread_rwlock_destroy(&rib.flows_lock);
+                pthread_mutex_destroy(&rib.cdap_reqs_lock);
+                pthread_mutex_destroy(&rib.ro_lock);
+                pthread_mutex_destroy(&rib.subs_lock);
+                free(rib.root);
                 return -1;
         }
 
         return 0;
 }
 
+static void rtree_destroy(struct rnode * node)
+{
+        if (node != NULL) {
+                rtree_destroy(node->child);
+                rtree_destroy(node->sibling);
+                free(node->name);
+                if (node->data != NULL)
+                        free(node->data);
+                free(node);
+        }
+}
+
 int ribmgr_fini()
 {
         struct list_head * pos = NULL;
@@ -159,7 +250,16 @@ int ribmgr_fini()
         if (rib.addr_auth != NULL)
                 addr_auth_destroy(rib.addr_auth);
 
+        pthread_mutex_lock(&rib.ro_lock);
+        rtree_destroy(rib.root->child);
+        free(rib.root);
+        pthread_mutex_unlock(&rib.ro_lock);
+
+        bmp_destroy(rib.sids);
+
+        pthread_mutex_destroy(&rib.subs_lock);
         pthread_mutex_destroy(&rib.cdap_reqs_lock);
+        pthread_mutex_destroy(&rib.ro_lock);
         pthread_rwlock_destroy(&rib.flows_lock);
 
         return 0;
@@ -574,3 +674,411 @@ uint64_t ribmgr_address()
 {
         return rib.address;
 }
+
+int ro_create(const char * name,
+              uint8_t *    data,
+              size_t       len)
+{
+        char * str, * str1, * saveptr, * token;
+        struct rnode * node, * new, * prev;
+        bool sibling;
+        struct list_head * p = NULL;
+        size_t len_s, len_n;
+        uint8_t * ro_data;
+
+        str = strdup(name);
+        if (str == NULL)
+                return -1;
+
+        pthread_mutex_lock(&rib.ro_lock);
+        node = rib.root;
+
+        for (str1 = str; ; str1 = NULL) {
+                token = strtok_r(str1, PATH_DELIMITER, &saveptr);
+                if (token == NULL) {
+                        pthread_mutex_unlock(&rib.ro_lock);
+                        LOG_ERR("RO already exists.");
+                        free(str);
+                        return -1;
+                }
+
+                prev = node;
+                node = node->child;
+                sibling = false;
+
+                /* Search horizontally */
+                while (node != NULL) {
+                        if (strcmp(node->name, token) == 0) {
+                                break;
+                        } else {
+                                prev = node;
+                                node = node->sibling;
+                                sibling = true;
+                        }
+                }
+
+                if (node == NULL)
+                        break;
+        }
+
+        token = strtok_r(str1, PATH_DELIMITER, &saveptr);
+        if (token != NULL) {
+                pthread_mutex_unlock(&rib.ro_lock);
+                LOG_ERR("Part of the pathname does not exist.");
+                free(str);
+                return -1;
+        }
+
+        free(str);
+
+        new = malloc(sizeof(*new));
+        if (new == NULL) {
+                pthread_mutex_unlock(&rib.ro_lock);
+                return -1;
+        }
+
+        new->name = strdup(token);
+        if (new->name == NULL) {
+                pthread_mutex_unlock(&rib.ro_lock);
+                free(new);
+                return -1;
+        }
+
+        if (sibling)
+                prev->sibling = new;
+        else
+                prev->child = new;
+
+        new->data = data;
+        new->len = len;
+        new->child = NULL;
+        new->sibling = NULL;
+
+        pthread_mutex_lock(&rib.subs_lock);
+
+        list_for_each(p, &rib.subs) {
+                struct ro_sub * e = list_entry(p, struct ro_sub, next);
+                len_s = strlen(e->name);
+                len_n = strlen(name);
+
+                if (len_n < len_s)
+                        continue;
+
+                if (strncmp(name, e->name, len_s) == 0) {
+                        if (e->ops->ro_created == NULL)
+                                continue;
+
+                        ro_data = malloc(len);
+                        if (ro_data == NULL)
+                                continue;
+
+                        memcpy(ro_data, data, len);
+                        e->ops->ro_created(name, ro_data, len);
+                }
+        }
+
+        pthread_mutex_unlock(&rib.subs_lock);
+        pthread_mutex_unlock(&rib.ro_lock);
+
+        return 0;
+}
+
+int ro_delete(const char * name)
+{
+        char * str, * str1, * saveptr, * token;
+        struct rnode * node, * prev;
+        bool sibling = false;
+        struct list_head * p = NULL;
+        size_t len_s, len_n;
+
+        str = strdup(name);
+        if (str == NULL)
+                return -1;
+
+        pthread_mutex_lock(&rib.ro_lock);
+
+        node = rib.root;
+        prev = NULL;
+
+        for (str1 = str; ; str1 = NULL) {
+                token = strtok_r(str1, PATH_DELIMITER, &saveptr);
+                if (token == NULL)
+                        break;
+
+                prev = node;
+                node = node->child;
+                sibling = false;
+
+                while (node != NULL) {
+                        if (strcmp(node->name, token) == 0) {
+                                break;
+                        } else {
+                                prev = node;
+                                node = node->sibling;
+                                sibling = true;
+                        }
+                }
+
+                if (node == NULL) {
+                        pthread_mutex_unlock(&rib.ro_lock);
+                        free(str);
+                        return -1;
+                }
+        }
+
+        if (node == rib.root) {
+                LOG_ERR("Won't remove root.");
+                free(str);
+                return -1;
+        }
+
+        free(node->name);
+        if (node->data != NULL)
+                free(node->data);
+
+        if (sibling)
+                prev->sibling = node->sibling;
+        else
+                prev->child = node->sibling;
+
+        free(node);
+
+        pthread_mutex_lock(&rib.subs_lock);
+
+        list_for_each(p, &rib.subs) {
+                struct ro_sub * e = list_entry(p, struct ro_sub, next);
+                len_s = strlen(e->name);
+                len_n = strlen(name);
+
+                if (len_n < len_s)
+                        continue;
+
+                if (strncmp(name, e->name, len_s) == 0) {
+                        if (e->ops->ro_deleted == NULL)
+                                continue;
+
+                        e->ops->ro_deleted(name);
+                }
+        }
+
+        pthread_mutex_unlock(&rib.subs_lock);
+        pthread_mutex_unlock(&rib.ro_lock);
+
+        free(str);
+        return 0;
+}
+
+static struct rnode * find_rnode_by_name(const char * name)
+{
+        char * str, * str1, * saveptr, * token;
+        struct rnode * node;
+
+        str = strdup(name);
+        if (str == NULL)
+                return NULL;
+
+        node = rib.root;
+
+        for (str1 = str; ; str1 = NULL) {
+                token = strtok_r(str1, PATH_DELIMITER, &saveptr);
+                if (token == NULL)
+                        break;
+
+                node = node->child;
+
+                while (node != NULL)
+                        if (strcmp(node->name, token) == 0)
+                                break;
+                        else
+                                node = node->sibling;
+
+                if (node == NULL) {
+                        free(str);
+                        return NULL;
+                }
+        }
+
+        free(str);
+        return node;
+}
+
+ssize_t ro_read(const char * name,
+                uint8_t **   data)
+{
+        struct rnode * node;
+        ssize_t        len;
+
+        pthread_mutex_lock(&rib.ro_lock);
+
+        node = find_rnode_by_name(name);
+        if (node == NULL) {
+                pthread_mutex_unlock(&rib.ro_lock);
+                return -1;
+        }
+
+        *data = malloc(node->len);
+        if (*data == NULL) {
+                pthread_mutex_unlock(&rib.ro_lock);
+                return -1;
+        }
+
+        memcpy(*data, node->data, node->len);
+        len = node->len;
+
+        pthread_mutex_unlock(&rib.ro_lock);
+
+        return len;
+}
+
+int ro_write(const char * name,
+             uint8_t *    data,
+             size_t       len)
+{
+        struct rnode * node;
+        struct list_head * p = NULL;
+        size_t len_s, len_n;
+        uint8_t * ro_data;
+
+        pthread_mutex_lock(&rib.ro_lock);
+
+        node = find_rnode_by_name(name);
+        if (node == NULL) {
+                pthread_mutex_unlock(&rib.ro_lock);
+                return -1;
+        }
+
+        free(node->data);
+
+        node->data = data;
+        node->len = len;
+
+        pthread_mutex_lock(&rib.subs_lock);
+
+        list_for_each(p, &rib.subs) {
+                struct ro_sub * e =
+                        list_entry(p, struct ro_sub, next);
+                len_s = strlen(e->name);
+                len_n = strlen(name);
+
+                if (len_n < len_s)
+                        continue;
+
+                if (strncmp(name, e->name, len_s) == 0) {
+                        if (e->ops->ro_updated == NULL)
+                                continue;
+
+                        ro_data = malloc(len);
+                        if (ro_data == NULL)
+                                continue;
+
+                        memcpy(ro_data, data, len);
+                        e->ops->ro_updated(name, ro_data, len);
+                }
+        }
+
+        pthread_mutex_unlock(&rib.subs_lock);
+        pthread_mutex_unlock(&rib.ro_lock);
+
+        return 0;
+}
+
+int ro_props(const char *      name,
+             struct ro_props * props)
+{
+        struct rnode * node;
+
+        pthread_mutex_lock(&rib.ro_lock);
+
+        node = find_rnode_by_name(name);
+        if (node == NULL) {
+                pthread_mutex_unlock(&rib.ro_lock);
+                return -1;
+        }
+
+        if (node->props != NULL) {
+                if (node->props->expiry != NULL)
+                        free(node->props->expiry);
+                free(node->props);
+        }
+
+        node->props = props;
+
+        pthread_mutex_unlock(&rib.ro_lock);
+
+        return 0;
+}
+
+int ro_sync(const char * name)
+{
+        (void) name;
+
+        LOG_MISSING;
+        /* FIXME: We need whatevercast sets first */
+
+        return -1;
+}
+
+int ro_subscribe(const char *        name,
+                 struct ro_sub_ops * ops)
+{
+        struct ro_sub * sub;
+
+        if (name == NULL || ops == NULL)
+                return -EINVAL;
+
+        sub = malloc(sizeof(*sub));
+        if (sub == NULL)
+                return -ENOMEM;
+
+        INIT_LIST_HEAD(&sub->next);
+
+        sub->name = strdup(name);
+        if (sub->name == NULL) {
+                free(sub);
+                return -1;
+        }
+
+        sub->ops = ops;
+
+        pthread_mutex_lock(&rib.subs_lock);
+
+        sub->sid = bmp_allocate(rib.sids);
+        if (sub->sid < 0) {
+                pthread_mutex_unlock(&rib.subs_lock);
+                free(sub->name);
+                free(sub);
+                LOG_ERR("Failed to get sub id.");
+        }
+
+        list_add(&sub->next, &rib.subs);
+
+        pthread_mutex_unlock(&rib.subs_lock);
+
+        return 0;
+}
+
+int ro_unsubscribe(int sid)
+{
+        struct list_head * pos = NULL;
+        struct list_head * n   = NULL;
+
+        pthread_mutex_lock(&rib.subs_lock);
+
+        list_for_each_safe(pos, n, &(rib.subs)) {
+                struct ro_sub * e = list_entry(pos, struct ro_sub, next);
+                if (sid == e->sid) {
+                        bmp_release(rib.sids, sid);
+                        list_del(&e->next);
+                        free(e->name);
+                        free(e);
+                        pthread_mutex_unlock(&rib.subs_lock);
+                        return 0;
+                }
+        }
+
+        pthread_mutex_unlock(&rib.subs_lock);
+
+        LOG_ERR("No such subscription found.");
+
+        return -1;
+}
diff --git a/src/ipcpd/normal/ribmgr.h b/src/ipcpd/normal/ribmgr.h
index 556a399f..36f771c1 100644
--- a/src/ipcpd/normal/ribmgr.h
+++ b/src/ipcpd/normal/ribmgr.h
@@ -38,10 +38,6 @@ int               ribmgr_remove_flow(int fd);
 
 int               ribmgr_bootstrap(struct dif_config * conf);
 
-/*
- * FIXME: Should we expose the RIB?
- * Else we may end up with a lot of getters and setters
- */
 struct dt_const * ribmgr_dt_const(void);
 
 uint64_t          ribmgr_address(void);
diff --git a/src/ipcpd/normal/ro.h b/src/ipcpd/normal/ro.h
new file mode 100644
index 00000000..0dfa7e8a
--- /dev/null
+++ b/src/ipcpd/normal/ro.h
@@ -0,0 +1,77 @@
+/*
+ * Ouroboros - Copyright (C) 2016
+ *
+ * RIB objects
+ *
+ *    Sander Vrijders <sander.vrijders@intec.ugent.be>
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef OUROBOROS_IPCP_RO_H
+#define OUROBOROS_IPCP_RO_H
+
+enum ro_recv_set {
+        ALL_MEMBERS = 0,
+        NEIGHBORS
+};
+
+struct ro_props {
+        bool              enrol_sync;
+        enum ro_recv_set  recv_set;
+        struct timespec * expiry;
+};
+
+/* All RIB-objects have a pathname, separated by a slash. */
+/* Takes ownership of the data */
+int          ro_create(const char * name,
+                       uint8_t *    data,
+                       size_t       len);
+
+int          ro_delete(const char * name);
+
+/* Reader takes ownership of data */
+ssize_t      ro_read(const char * name,
+                     uint8_t **   data);
+
+int          ro_write(const char * name,
+                      uint8_t *    data,
+                      size_t       len);
+
+/* Takes ownership of the props */
+int          ro_props(const char *      name,
+                      struct ro_props * props);
+
+/* Sync changes with other members in the DIF */
+int          ro_sync(const char * name);
+
+/* Callback passes ownership of the data */
+struct ro_sub_ops {
+        int (* ro_created)(const char * name,
+                           uint8_t *    data,
+                           size_t       len);
+        int (* ro_updated)(const char * name,
+                           uint8_t *    data,
+                           size_t       len);
+        int (* ro_deleted)(const char * name);
+};
+
+/* Returns subscriber-id */
+int          ro_subscribe(const char *        name,
+                          struct ro_sub_ops * ops);
+
+int          ro_unsubscribe(int sid);
+
+#endif
-- 
cgit v1.2.3