From f86502516b2a069954d0529d8b43593ece7360eb Mon Sep 17 00:00:00 2001 From: dimitri staessens Date: Mon, 18 Sep 2017 08:03:31 +0200 Subject: lib: Provide RIB API to export internals via fuse This adds a virtual RIB that is accessible as a filesystem that is accessed through a fuse mountpoint (configurable , default is /tmp/ouroboros). Currently, each IPCP will export its link state database. --- include/ouroboros/rib.h | 46 +++++ src/ipcpd/normal/main.c | 21 +- src/ipcpd/normal/pol/link_state.c | 124 +++++++++++- src/irmd/config.h.in | 5 + src/irmd/main.c | 12 +- src/lib/CMakeLists.txt | 25 ++- src/lib/config.h.in | 7 +- src/lib/rib.c | 389 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 611 insertions(+), 18 deletions(-) create mode 100644 include/ouroboros/rib.h create mode 100644 src/lib/rib.c diff --git a/include/ouroboros/rib.h b/include/ouroboros/rib.h new file mode 100644 index 00000000..9642cb24 --- /dev/null +++ b/include/ouroboros/rib.h @@ -0,0 +1,46 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2017 + * + * RIB export using FUSE + * + * Dimitri Staessens + * Sander Vrijders + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_LIB_RIB_H +#define OUROBOROS_LIB_RIB_H + +#define RIB_PATH_LEN 128 + +struct rib; + +struct rib_ops { + int (* read)(const char * path, + char * buf, + size_t len); + int (* readdir)(char *** entries); +}; + +int rib_init(const char * prefix); + +void rib_fini(void); + +int rib_reg(const char * path, + struct rib_ops * ops); + +void rib_unreg(const char * path); + +#endif /* OUROBOROS_LIB_RIB_H */ diff --git a/src/ipcpd/normal/main.c b/src/ipcpd/normal/main.c index 2b35a04a..e6dd6717 100644 --- a/src/ipcpd/normal/main.c +++ b/src/ipcpd/normal/main.c @@ -26,21 +26,21 @@ #define OUROBOROS_PREFIX "normal-ipcp" -#include -#include +#include +#include #include -#include #include -#include -#include +#include #include +#include +#include #include "addr_auth.h" #include "connmgr.h" #include "dir.h" +#include "dt.h" #include "enroll.h" #include "fa.h" -#include "dt.h" #include "ipcp.h" #include @@ -338,6 +338,11 @@ int main(int argc, } /* These components must be init at creation. */ + if (rib_init("ipcpd-normal")) { + log_err("Failed to initialize RIB."); + goto fail_rib_init; + } + if (connmgr_init()) { log_err("Failed to initialize connection manager."); goto fail_connmgr_init; @@ -378,6 +383,8 @@ int main(int argc, connmgr_fini(); + rib_fini(); + irm_unbind_api(getpid(), ipcpi.name); ipcp_fini(); @@ -393,6 +400,8 @@ int main(int argc, fail_enroll_init: connmgr_fini(); fail_connmgr_init: + rib_fini(); + fail_rib_init: irm_unbind_api(getpid(), ipcpi.name); fail_bind_api: ipcp_fini(); diff --git a/src/ipcpd/normal/pol/link_state.c b/src/ipcpd/normal/pol/link_state.c index 7df09bce..3d9e8246 100644 --- a/src/ipcpd/normal/pol/link_state.c +++ b/src/ipcpd/normal/pol/link_state.c @@ -22,15 +22,18 @@ #define _POSIX_C_SOURCE 200112L +#include "config.h" + #define OUROBOROS_PREFIX "link-state-routing" +#include #include +#include #include #include -#include #include -#include -#include +#include +#include #include "ae.h" #include "connmgr.h" @@ -52,6 +55,7 @@ typedef LinkStateMsg link_state_msg_t; #define LS_UPDATE_TIME 15 #define LS_TIMEO 60 #define LSA_MAX_LEN 128 +#define LSDB "lsdb" #ifndef CLOCK_REALTIME_COARSE #define CLOCK_REALTIME_COARSE CLOCK_REALTIME @@ -90,6 +94,7 @@ struct { fset_t * mgmt_set; struct list_head db; + size_t db_len; pthread_rwlock_t db_lock; @@ -107,6 +112,106 @@ struct pol_routing_ops link_state_ops = { .routing_i_destroy = link_state_routing_i_destroy }; +static int str_adj(struct adjacency * adj, + char * buf, + size_t len) +{ + char tmbuf[64]; + struct tm * tm; + + if (len < 256) + return -1; + + tm = localtime(&adj->stamp); + strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", tm); + + sprintf(buf, + "src: %" PRIu64 "\n" + "dst: %" PRIu64 "\n" + "upd: %s\n", + adj->src, + adj->dst, + tmbuf); + + return strlen(buf); +} + +static int lsdb_read(const char * path, + char * buf, + size_t len) +{ + struct list_head * p; + char entry[RIB_PATH_LEN + 1]; + + pthread_rwlock_rdlock(&ls.db_lock); + + if (ls.db_len == 0) { + pthread_rwlock_unlock(&ls.db_lock); + return -EPERM; + } + + list_for_each(p, &ls.db) { + struct adjacency * a = list_entry(p, struct adjacency, next); + sprintf(entry, "%" PRIu64 ".%" PRIu64, a->src, a->dst); + if (strcmp(entry, path) == 0) { + len = str_adj(a, buf, len); + pthread_rwlock_unlock(&ls.db_lock); + return len; + } + } + + pthread_rwlock_unlock(&ls.db_lock); + + return -1; +} + +static int lsdb_readdir(char *** buf) +{ + struct list_head * p; + char entry[RIB_PATH_LEN + 1]; + ssize_t idx = 0; + + pthread_rwlock_rdlock(&ls.db_lock); + + if (ls.db_len == 0) { + pthread_rwlock_unlock(&ls.db_lock); + return 0; + } + + *buf = malloc(sizeof(**buf) * ls.db_len); + if (*buf == NULL) { + pthread_rwlock_unlock(&ls.db_lock); + return -ENOMEM; + } + + list_for_each(p, &ls.db) { + struct adjacency * a = list_entry(p, struct adjacency, next); + sprintf(entry, "%" PRIu64 ".%" PRIu64, a->src, a->dst); + (*buf)[idx] = malloc(strlen(entry) + 1); + if ((*buf)[idx] == NULL) { + ssize_t j; + for (j = 0; j < idx; ++j) + free(*buf[j]); + free(buf); + pthread_rwlock_unlock(&ls.db_lock); + return -ENOMEM; + } + + strcpy((*buf)[idx], entry); + + idx++; + } + + pthread_rwlock_unlock(&ls.db_lock); + + return idx; +} + +static struct rib_ops r_ops = { + .read = lsdb_read, + .readdir = lsdb_readdir +}; + static int lsdb_add_nb(uint64_t addr, int fd, enum nb_type type) @@ -214,11 +319,11 @@ static int lsdb_add_link(uint64_t src, list_add_tail(&adj->next, p); + ls.db_len++; + if (graph_update_edge(ls.graph, src, dst, *qs)) log_warn("Failed to add edge to graph."); - log_dbg("Added %" PRIu64 " - %" PRIu64" to lsdb.", adj->src, adj->dst); - pthread_rwlock_unlock(&ls.db_lock); return 0; @@ -239,8 +344,7 @@ static int lsdb_del_link(uint64_t src, if (graph_del_edge(ls.graph, src, dst)) log_warn("Failed to delete edge from graph."); - log_dbg("Removed %" PRIu64 " - %" PRIu64" from lsdb.", - a->src, a->dst); + ls.db_len--; pthread_rwlock_unlock(&ls.db_lock); free(a); @@ -587,6 +691,10 @@ int link_state_init(void) if (pthread_create(&ls.listener, NULL, ls_conn_handle, NULL)) goto fail_pthread_create_listener; + ls.db_len = 0; + + rib_reg(LSDB, &r_ops); + return 0; fail_pthread_create_listener: @@ -614,6 +722,8 @@ void link_state_fini(void) struct list_head * p; struct list_head * h; + rib_unreg(LSDB); + pthread_cancel(ls.listener); pthread_join(ls.listener, NULL); diff --git a/src/irmd/config.h.in b/src/irmd/config.h.in index c217fe93..d253a1ac 100644 --- a/src/irmd/config.h.in +++ b/src/irmd/config.h.in @@ -45,3 +45,8 @@ #define IRMD_MIN_THREADS @IRMD_MIN_THREADS@ #define IRMD_ADD_THREADS @IRMD_ADD_THREADS@ + +#cmakedefine HAVE_FUSE +#ifdef HAVE_FUSE +#define FUSE_PREFIX "@FUSE_PREFIX@" +#endif diff --git a/src/irmd/main.c b/src/irmd/main.c index c5caff6c..df903f40 100644 --- a/src/irmd/main.c +++ b/src/irmd/main.c @@ -1631,6 +1631,11 @@ static void irm_fini(void) pthread_cond_destroy(&irmd.cmd_cond); pthread_rwlock_destroy(&irmd.reg_lock); pthread_rwlock_destroy(&irmd.state_lock); + +#ifdef HAVE_FUSE + if (rmdir(FUSE_PREFIX)) + log_dbg("Failed to remove " FUSE_PREFIX); +#endif } void irmd_sig_handler(int sig, @@ -2249,7 +2254,12 @@ static int irm_init(void) log_err("Failed to create rdrbuff."); goto fail_rdrbuff; } - +#ifdef HAVE_FUSE + if (stat(FUSE_PREFIX, &st) != -1) + log_warn(FUSE_PREFIX " already exists..."); + else + mkdir(FUSE_PREFIX, 0777); +#endif irmd.csockfd = -1; irmd.state = IRMD_RUNNING; diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index fd7ece83..26cecb44 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -37,10 +37,30 @@ if (HAVE_ROBUST_MUTEX) set(HAVE_ROBUST_MUTEX TRUE) else () message(STATUS "Robust mutex support disabled by user") - set(HAVE_ROBUST_MUTEX FALSE) endif () endif () +find_library(FUSE_LIBRARIES fuse QUIET) +if (FUSE_LIBRARIES) + #FIXME: Check for version >= 2.6 + set(DISABLE_FUSE FALSE CACHE BOOL "Disable FUSE support") + if (NOT DISABLE_FUSE) + message(STATUS "FUSE support enabled") + set(FUSE_PREFIX "/tmp/ouroboros" CACHE STRING + "Mountpoint for RIB filesystem") + set(HAVE_FUSE TRUE CACHE INTERNAL "") + else () + message(STATUS "FUSE support disabled by user") + endif () +endif () + +if (NOT HAVE_FUSE) + set(FUSE_LIBRARIES "") + set(FUSE_INCLUDE_DIR "") +endif () + +mark_as_advanced(FUSE_LIBRARIES) + find_library(LIBGCRYPT_LIBRARIES gcrypt QUIET) if (LIBGCRYPT_LIBRARIES) find_path(LIBGCRYPT_INCLUDE_DIR gcrypt.h @@ -163,6 +183,7 @@ set(SOURCE_FILES qos.c qoscube.c random.c + rib.c rq.c sha3.c shm_flow_set.c @@ -188,7 +209,7 @@ endif (CMAKE_BUILD_TYPE MATCHES Debug) target_link_libraries(ouroboros ${LIBRT_LIBRARIES} ${LIBPTHREAD_LIBRARIES} ${PROTOBUF_C_LIBRARY} ${OPENSSL_LIBRARIES} - ${LIBGCRYPT_LIBRARIES}) + ${LIBGCRYPT_LIBRARIES} ${FUSE_LIBRARIES}) install(TARGETS ouroboros LIBRARY DESTINATION usr/lib) diff --git a/src/lib/config.h.in b/src/lib/config.h.in index e9c43389..a85ce7b4 100644 --- a/src/lib/config.h.in +++ b/src/lib/config.h.in @@ -45,6 +45,11 @@ #cmakedefine HAVE_ROBUST_MUTEX #endif +#cmakedefine HAVE_FUSE +#ifdef HAVE_FUSE +#define FUSE_PREFIX "@FUSE_PREFIX@" +#endif + #define PTHREAD_COND_CLOCK @PTHREAD_COND_CLOCK@ #define AP_MAX_FLOWS @AP_MAX_FLOWS@ @@ -53,5 +58,3 @@ #define DU_BUFF_HEADSPACE @DU_BUFF_HEADSPACE@ #define DU_BUFF_TAILSPACE @DU_BUFF_TAILSPACE@ - -#define CDAP_REPLY_TIMEOUT @CDAP_REPLY_TIMEOUT@ diff --git a/src/lib/rib.c b/src/lib/rib.c new file mode 100644 index 00000000..b6c8e140 --- /dev/null +++ b/src/lib/rib.c @@ -0,0 +1,389 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2017 + * + * RIB export using FUSE + * + * Dimitri Staessens + * Sander Vrijders + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200112L + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_FUSE +#define _FILE_OFFSET_BITS 64 +#define FUSE_USE_VERSION 26 +#include +#endif + +#ifdef HAVE_FUSE + +#ifndef CLOCK_REALTIME_COARSE +#define CLOCK_REALTIME_COARSE CLOCK_REALTIME +#endif + +#define RT "/" + +struct reg_comp { + struct list_head next; + + char path[RIB_PATH_LEN + 1]; + struct rib_ops * ops; +}; + +struct { + struct list_head reg_comps; + + char mnt[RIB_PATH_LEN + 1]; + + struct fuse * fuse; + struct fuse_chan * ch; + + pthread_rwlock_t lock; + + pthread_t fuse_thr; +} rib; + +static int rib_open(const char * path, + struct fuse_file_info * info) +{ + (void) path; + + info->nonseekable = 1; + + return 0; +} + +static int rib_opendir(const char * path, + struct fuse_file_info * info) +{ + (void) path; + (void) info; + + return 0; +} + +static int rib_read(const char * path, + char * buf, + size_t size, + off_t offset, + struct fuse_file_info * info) +{ + struct list_head * p; + char comp[RIB_PATH_LEN + 1]; + char * c; + + strcpy(comp, path + 1); + + c = strstr(comp, "/"); + + if (c != NULL) + *c = '\0'; + + (void) info; + (void) offset; + + pthread_rwlock_wrlock(&rib.lock); + + list_for_each(p, &rib.reg_comps) { + struct reg_comp * r = list_entry(p, struct reg_comp, next); + if (strcmp(comp, r->path) == 0) { + int ret = r->ops->read(c + 1, buf, size); + pthread_rwlock_unlock(&rib.lock); + return ret; + } + } + + pthread_rwlock_unlock(&rib.lock); + + return -1; +} + +static int rib_readdir(const char * path, + void * buf, + fuse_fill_dir_t filler, + off_t offset, + struct fuse_file_info * info) +{ + struct list_head * p; + + (void) offset; + (void) info; + + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + + pthread_rwlock_rdlock(&rib.lock); + + if (strcmp(path, RT) == 0) { + list_for_each(p, &rib.reg_comps) { + struct reg_comp * c; + c = list_entry(p, struct reg_comp, next); + filler(buf, c->path, NULL, 0); + } + } else { + list_for_each(p, &rib.reg_comps) { + char ** dir_entries; + ssize_t len; + ssize_t i; + struct reg_comp * c; + c = list_entry(p, struct reg_comp, next); + if (strcmp(path + 1, c->path) == 0) + if (c->ops->readdir == NULL) + break; + + len = c->ops->readdir(&dir_entries); + if (len < 0) + break; + for (i = 0; i < len; ++i) + filler(buf, dir_entries[i], NULL, 0); + freepp(char, dir_entries, len); + } + } + + pthread_rwlock_unlock(&rib.lock); + + return 0; +} + +static int rib_getattr(const char * path, + struct stat * st) +{ + struct list_head * p; + struct timespec now; + + clock_gettime(CLOCK_REALTIME_COARSE, &now); + + memset(st, 0, sizeof(*st)); + + if (strcmp(path, RT) == 0) { + st->st_mode = __S_IFDIR | 0755; + st->st_nlink = 2; + st->st_uid = getuid(); + st->st_gid = getgid(); + st->st_mtime = now.tv_sec; + return 0; + } + + pthread_rwlock_rdlock(&rib.lock); + + list_for_each(p, &rib.reg_comps) { + struct reg_comp * rc = list_entry(p, struct reg_comp, next); + if (strcmp(path + 1, rc->path) == 0) { + st->st_mode = __S_IFDIR | 0755; + st->st_nlink = 2; + break; + } + } + + pthread_rwlock_unlock(&rib.lock); + + if (st->st_mode == 0) { + char buf[4096]; + st->st_nlink = 2; + st->st_mode = __S_IFREG | 0755; + st->st_size = rib_read(path, buf, 4096, 0, NULL); + } + + st->st_uid = getuid(); + st->st_gid = getgid(); + st->st_mtime = now.tv_sec; + + return 0; +} + +static struct fuse_operations r_ops = { + .getattr = rib_getattr, + .open = rib_open, + .opendir = rib_opendir, + .read = rib_read, + .readdir = rib_readdir +}; + +static void * fuse_thr(void * o) +{ + (void) o; + + if (fuse_loop(rib.fuse) < 0) + return (void *) -1; + + return (void *) 0; +} +#endif /* HAVE_FUSE */ + +int rib_init(const char * prefix) +{ +#ifdef HAVE_FUSE + struct stat st; + char * argv[] = {"ignored", + NULL, + "-f", + "-o", + "ro,", + "allow_other,", + "default_permissions,", + "fsname=rib", + NULL}; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + + if (stat(FUSE_PREFIX, &st) == -1) + return -1; + + sprintf(rib.mnt, FUSE_PREFIX "/%s.%d", prefix, getpid()); + + if (stat(rib.mnt, &st) == -1) + mkdir(rib.mnt, 0777); + + argv[1] = rib.mnt; + + fuse_opt_parse(&args, argv, NULL, NULL); + + list_head_init(&rib.reg_comps); + + rib.ch = fuse_mount(rib.mnt, &args); + if (rib.ch == NULL) + goto fail_mount; + + rib.fuse = fuse_new(rib.ch, &args, &r_ops, sizeof(r_ops), NULL); + if (rib.fuse == NULL) + goto fail_fuse; + + if (pthread_rwlock_init(&rib.lock, NULL)) + goto fail_rwlock_init; + + if (pthread_create(&rib.fuse_thr, NULL, fuse_thr, NULL)) + goto fail_fuse_thr; + + fuse_opt_free_args(&args); + + return 0; + + fail_fuse_thr: + pthread_rwlock_destroy(&rib.lock); + fail_rwlock_init: + fuse_destroy(rib.fuse); + fail_fuse: + fuse_unmount(rib.mnt, rib.ch); + fail_mount: + fuse_opt_free_args(&args); + rmdir(rib.mnt); + return -1; +#else + (void) prefix; + return 0; +#endif +} + +void rib_fini(void) +{ +#ifdef HAVE_FUSE + struct list_head * p; + struct list_head * h; + + fuse_unmount(rib.mnt, rib.ch); + pthread_join(rib.fuse_thr, NULL); + + fuse_destroy(rib.fuse); + + rmdir(rib.mnt); + + pthread_rwlock_wrlock(&rib.lock); + + list_for_each_safe(p, h, &rib.reg_comps) { + struct reg_comp * c = list_entry(p, struct reg_comp, next); + list_del(&c->next); + free(c); + } + + pthread_rwlock_unlock(&rib.lock); + + pthread_rwlock_destroy(&rib.lock); +#endif +} + +int rib_reg(const char * path, + struct rib_ops * ops) +{ +#ifdef HAVE_FUSE + struct reg_comp * rc; + struct list_head * p; + + pthread_rwlock_wrlock(&rib.lock); + + list_for_each(p, &rib.reg_comps) { + struct reg_comp * r = list_entry(p, struct reg_comp, next); + if (strcmp(r->path, path) == 0) { + pthread_rwlock_unlock(&rib.lock); + return -EPERM; + } + + if (strcmp(r->path, path) > 0) + break; + } + + rc = malloc(sizeof(*rc)); + if (rc == NULL) { + pthread_rwlock_unlock(&rib.lock); + return -ENOMEM; + } + + strcpy(rc->path, path); + rc->ops = ops; + + list_add_tail(&rc->next, p); + + pthread_rwlock_unlock(&rib.lock); +#else + (void) path; + (void) ops; +#endif + return 0; +} + +void rib_unreg(const char * path) +{ +#ifdef HAVE_FUSE + struct list_head * p; + struct list_head * h; + + pthread_rwlock_wrlock(&rib.lock); + + list_for_each_safe(p, h, &rib.reg_comps) { + struct reg_comp * r = list_entry(p, struct reg_comp, next); + if (strcmp(r->path, path) == 0) { + list_del(&r->next); + free(r); + break; + } + } + + pthread_rwlock_unlock(&rib.lock); +#else + (void) path; +#endif +} -- cgit v1.2.3