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. --- src/lib/CMakeLists.txt | 25 +++- src/lib/config.h.in | 7 +- src/lib/rib.c | 389 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 417 insertions(+), 4 deletions(-) create mode 100644 src/lib/rib.c (limited to 'src/lib') 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