summaryrefslogtreecommitdiff
path: root/src/ipcpd/broadcast
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipcpd/broadcast')
-rw-r--r--src/ipcpd/broadcast/CMakeLists.txt35
-rw-r--r--src/ipcpd/broadcast/connmgr.c35
-rw-r--r--src/ipcpd/broadcast/dt.c322
-rw-r--r--src/ipcpd/broadcast/dt.h27
-rw-r--r--src/ipcpd/broadcast/main.c321
5 files changed, 740 insertions, 0 deletions
diff --git a/src/ipcpd/broadcast/CMakeLists.txt b/src/ipcpd/broadcast/CMakeLists.txt
new file mode 100644
index 00000000..d85f335e
--- /dev/null
+++ b/src/ipcpd/broadcast/CMakeLists.txt
@@ -0,0 +1,35 @@
+get_filename_component(CURRENT_SOURCE_PARENT_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
+get_filename_component(CURRENT_BINARY_PARENT_DIR
+ ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+include_directories(${CURRENT_SOURCE_PARENT_DIR})
+include_directories(${CURRENT_BINARY_PARENT_DIR})
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_BINARY_DIR}/include)
+
+set(IPCP_BROADCAST_TARGET ipcpd-broadcast CACHE INTERNAL "")
+set(IPCP_BROADCAST_MPL 60 CACHE STRING
+ "Default maximum packet lifetime for the broadcast IPCP, in seconds")
+
+set(SOURCE_FILES
+ # Add source files here
+ connmgr.c
+ dt.c
+ main.c
+ )
+
+add_executable(ipcpd-broadcast ${SOURCE_FILES} ${IPCP_SOURCES} ${COMMON_SOURCES}
+ ${LAYER_CONFIG_PROTO_SRCS})
+target_link_libraries(ipcpd-broadcast LINK_PUBLIC ouroboros-dev)
+
+include(AddCompileFlags)
+if (CMAKE_BUILD_TYPE MATCHES "Debug*")
+ add_compile_flags(ipcpd-broadcast -DCONFIG_OUROBOROS_DEBUG)
+endif ()
+
+install(TARGETS ipcpd-broadcast RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
diff --git a/src/ipcpd/broadcast/connmgr.c b/src/ipcpd/broadcast/connmgr.c
new file mode 100644
index 00000000..f297175d
--- /dev/null
+++ b/src/ipcpd/broadcast/connmgr.c
@@ -0,0 +1,35 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Handles connections between components
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., http://www.fsf.org/about/contact/.
+ */
+
+#include "config.h"
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include <ouroboros/ipcp.h>
+
+#define BUILD_IPCP_BROADCAST
+
+#include "common/connmgr.c"
diff --git a/src/ipcpd/broadcast/dt.c b/src/ipcpd/broadcast/dt.c
new file mode 100644
index 00000000..938c9085
--- /dev/null
+++ b/src/ipcpd/broadcast/dt.c
@@ -0,0 +1,322 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Forward loop for broadcast
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include "config.h"
+
+#define BROADCAST_MTU 1400 /* FIXME: avoid packet copy. */
+
+#define DT "dt"
+#define OUROBOROS_PREFIX DT
+
+#include <ouroboros/dev.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/fqueue.h>
+#include <ouroboros/list.h>
+#include <ouroboros/logs.h>
+#include <ouroboros/notifier.h>
+#include <ouroboros/utils.h>
+#include <ouroboros/pthread.h>
+
+#include "common/comp.h"
+#include "common/connmgr.h"
+#include "dt.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+
+struct nb {
+ struct list_head next;
+
+ int fd;
+};
+
+struct {
+ struct list_head nbs;
+ size_t nbs_len;
+ pthread_rwlock_t nbs_lock;
+
+ fset_t * set;
+
+ pthread_t reader;
+ pthread_t listener;
+} fwd;
+
+static int dt_add_nb(int fd)
+{
+ struct list_head * p;
+ struct nb * nb;
+
+ pthread_rwlock_wrlock(&fwd.nbs_lock);
+
+ list_for_each(p, &fwd.nbs) {
+ struct nb * el = list_entry(p, struct nb, next);
+ if (el->fd == fd) {
+ pthread_rwlock_unlock(&fwd.nbs_lock);
+ log_warn("Already know neighbor on fd %d.", fd);
+ return 0;
+ }
+ }
+
+ nb = malloc(sizeof(*nb));
+ if (nb == NULL) {
+ pthread_rwlock_unlock(&fwd.nbs_lock);
+ log_err("Failed to malloc neighbor struct.");
+ return -ENOMEM;
+ }
+
+ nb->fd = fd;
+
+ list_add_tail(&nb->next, p);
+
+ ++fwd.nbs_len;
+
+ pthread_rwlock_unlock(&fwd.nbs_lock);
+
+ log_dbg("Neighbor %d added.", fd);
+
+ return 0;
+}
+
+static int dt_del_nb(int fd)
+{
+ struct list_head * p;
+ struct list_head * h;
+
+ pthread_rwlock_wrlock(&fwd.nbs_lock);
+
+ list_for_each_safe(p, h, &fwd.nbs) {
+ struct nb * nb = list_entry(p, struct nb, next);
+ if (nb->fd == fd) {
+ list_del(&nb->next);
+ --fwd.nbs_len;
+ pthread_rwlock_unlock(&fwd.nbs_lock);
+ log_dbg("Neighbor %d deleted.", nb->fd);
+ free(nb);
+ return 0;
+ }
+ }
+
+ pthread_rwlock_unlock(&fwd.nbs_lock);
+
+ log_err("Neighbor not found on fd %d.", fd);
+
+ return -EPERM;
+}
+
+static void * dt_conn_handle(void * o)
+{
+ struct conn conn;
+
+ (void) o;
+
+ while (true) {
+ if (connmgr_wait(COMPID_DT, &conn)) {
+ log_err("Failed to get next DT connection.");
+ continue;
+ }
+
+ /* NOTE: connection acceptance policy could be here. */
+
+ notifier_event(NOTIFY_DT_CONN_ADD, &conn);
+ }
+
+ return 0;
+}
+
+
+static void dt_packet(uint8_t * buf,
+ size_t len,
+ int in_fd)
+{
+ struct list_head * p;
+
+ pthread_rwlock_rdlock(&fwd.nbs_lock);
+
+ pthread_cleanup_push(__cleanup_rwlock_unlock, &fwd.nbs_lock);
+
+ list_for_each(p, &fwd.nbs) {
+ struct nb * nb = list_entry(p, struct nb, next);
+ if (nb->fd != in_fd)
+ flow_write(nb->fd, buf, len); /* FIXME: avoid copy. */
+ }
+
+ pthread_cleanup_pop(true);
+}
+
+static void __cleanup_fqueue_destroy(void * fq)
+{
+ fqueue_destroy((fqueue_t *) fq);
+}
+
+static void * dt_reader(void * o)
+{
+ fqueue_t * fq;
+ int ret;
+ uint8_t buf[BROADCAST_MTU];
+ int fd;
+ ssize_t len;
+
+ (void) o;
+
+ fq = fqueue_create();
+ if (fq == NULL)
+ return (void *) -1;
+
+ pthread_cleanup_push(__cleanup_fqueue_destroy, (void *) fq);
+
+ while (true) {
+ ret = fevent(fwd.set, fq, NULL);
+ if (ret < 0) {
+ log_warn("Event warning: %d.", ret);
+ continue;
+ }
+
+ while ((fd = fqueue_next(fq)) >= 0) {
+ if (fqueue_type(fq) != FLOW_PKT)
+ continue;
+
+ /* FIXME: avoid copy. */
+ len = flow_read(fd, buf, BROADCAST_MTU);
+ if (len < 0)
+ continue;
+
+ dt_packet(buf, len, fd);
+ }
+ }
+
+ pthread_cleanup_pop(true);
+
+ return (void *) 0;
+}
+
+static void handle_event(void * self,
+ int event,
+ const void * o)
+{
+ /* FIXME: Apply correct QoS on graph */
+ struct conn * c;
+
+ (void) self;
+
+ c = (struct conn *) o;
+
+ switch (event) {
+ case NOTIFY_DT_CONN_ADD:
+ if (dt_add_nb(c->flow_info.fd) < 0)
+ log_err("Failed to add neighbor.");
+ fset_add(fwd.set, c->flow_info.fd);
+ break;
+ case NOTIFY_DT_CONN_DEL:
+ if (dt_del_nb(c->flow_info.fd) < 0)
+ log_err("Failed to delete neighbor.");
+ fset_del(fwd.set, c->flow_info.fd);
+ break;
+ default:
+ break;
+ }
+}
+
+int dt_init(void)
+{
+ struct conn_info info;
+
+ memset(&info, 0, sizeof(info));
+
+ strcpy(info.comp_name, DT);
+ strcpy(info.comp_name, DT_COMP);
+
+ list_head_init(&fwd.nbs);
+
+ if (notifier_reg(handle_event, NULL))
+ goto fail_notifier_reg;
+
+ if (pthread_rwlock_init(&fwd.nbs_lock, NULL))
+ goto fail_lock_init;
+
+ fwd.set = fset_create();
+ if (fwd.set == NULL)
+ goto fail_fset_create;
+
+ if (pthread_create(&fwd.reader, NULL, dt_reader, NULL))
+ goto fail_pthread_create_reader;
+
+ if (pthread_create(&fwd.listener, NULL, dt_conn_handle, NULL))
+ goto fail_pthread_create_listener;
+
+ if (connmgr_comp_init(COMPID_DT, &info))
+ goto fail_connmgr_comp_init;
+
+ fwd.nbs_len = 0;
+
+ return 0;
+
+ fail_connmgr_comp_init:
+ pthread_cancel(fwd.listener);
+ pthread_join(fwd.listener, NULL);
+ fail_pthread_create_listener:
+ pthread_cancel(fwd.reader);
+ pthread_join(fwd.reader, NULL);
+ fail_pthread_create_reader:
+ fset_destroy(fwd.set);
+ fail_fset_create:
+ pthread_rwlock_destroy(&fwd.nbs_lock);
+ fail_lock_init:
+ notifier_unreg(handle_event);
+ fail_notifier_reg:
+ return -1;
+}
+
+void dt_fini(void)
+{
+ struct list_head * p;
+ struct list_head * h;
+
+ notifier_unreg(handle_event);
+
+ pthread_cancel(fwd.reader);
+ pthread_cancel(fwd.listener);
+
+ pthread_join(fwd.reader, NULL);
+ pthread_join(fwd.listener, NULL);
+
+ fset_destroy(fwd.set);
+
+ pthread_rwlock_wrlock(&fwd.nbs_lock);
+
+ list_for_each_safe(p, h, &fwd.nbs) {
+ struct nb * n = list_entry(p, struct nb, next);
+ list_del(&n->next);
+ free(n);
+ }
+
+ pthread_rwlock_unlock(&fwd.nbs_lock);
+
+ pthread_rwlock_destroy(&fwd.nbs_lock);
+}
diff --git a/src/ipcpd/broadcast/dt.h b/src/ipcpd/broadcast/dt.h
new file mode 100644
index 00000000..8d3b83f8
--- /dev/null
+++ b/src/ipcpd/broadcast/dt.h
@@ -0,0 +1,27 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Forward loop for broadcast
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., http://www.fsf.org/about/contact/.
+ */
+
+#define DT_COMP "Data Transfer"
+
+int dt_init(void);
+
+void dt_fini(void);
diff --git a/src/ipcpd/broadcast/main.c b/src/ipcpd/broadcast/main.c
new file mode 100644
index 00000000..f51fc629
--- /dev/null
+++ b/src/ipcpd/broadcast/main.c
@@ -0,0 +1,321 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Broadcast IPC Process
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+#include "config.h"
+
+#define OUROBOROS_PREFIX "broadcast-ipcp"
+#define THIS_TYPE IPCP_BROADCAST
+
+#include <ouroboros/dev.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/ipcp-dev.h>
+#include <ouroboros/logs.h>
+#include <ouroboros/notifier.h>
+#include <ouroboros/random.h>
+#include <ouroboros/rib.h>
+#include <ouroboros/time.h>
+
+#include "common/connmgr.h"
+#include "common/enroll.h"
+#include "dt.h"
+#include "ipcp.h"
+
+#include <stdbool.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <inttypes.h>
+
+struct ipcp ipcpi;
+
+static int initialize_components(const struct ipcp_config * conf)
+{
+ strcpy(ipcpi.layer_name, conf->layer_info.name);
+ ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo;
+
+ assert(ipcp_dir_hash_len() != 0);
+
+ if (dt_init() < 0) {
+ log_err("Failed to initialize forwarding component.");
+ return -1;
+ }
+
+ ipcp_set_state(IPCP_INIT);
+
+ return 0;
+}
+
+static void finalize_components(void)
+{
+ dt_fini();
+}
+
+static int start_components(void)
+{
+ if (enroll_start() < 0) {
+ log_err("Failed to start enrollment.");
+ goto fail_enroll_start;
+ }
+
+ if (connmgr_start() < 0) {
+ log_err("Failed to start AP connection manager.");
+ goto fail_connmgr_start;
+ }
+
+ return 0;
+
+ fail_connmgr_start:
+ enroll_stop();
+ fail_enroll_start:
+ ipcp_set_state(IPCP_INIT);
+ return -1;
+}
+
+static void stop_components(void)
+{
+ connmgr_stop();
+
+ enroll_stop();
+}
+
+static int broadcast_ipcp_enroll(const char * dst,
+ struct layer_info * info)
+{
+ struct conn conn;
+ uint8_t id[ENROLL_ID_LEN];
+
+ if (random_buffer(id, ENROLL_ID_LEN) < 0) {
+ log_err("Failed to generate enrollment ID.");
+ goto fail_id;
+ }
+
+ log_info_id(id, "Requesting enrollment.");
+
+ if (connmgr_alloc(COMPID_ENROLL, dst, NULL, &conn) < 0) {
+ log_err_id(id, "Failed to get connection.");
+ goto fail_id;
+ }
+
+ /* Get boot state from peer. */
+ if (enroll_boot(&conn, id) < 0) {
+ log_err_id(id, "Failed to get boot information.");
+ goto fail_enroll_boot;
+ }
+
+ if (initialize_components(enroll_get_conf()) < 0) {
+ log_err_id(id, "Failed to initialize components.");
+ goto fail_enroll_boot;
+ }
+
+ if (start_components() < 0) {
+ log_err_id(id, "Failed to start components.");
+ goto fail_start_comp;
+ }
+
+ if (enroll_ack(&conn, id, 0) < 0)
+ log_err_id(id, "Failed to confirm enrollment.");
+
+ if (connmgr_dealloc(COMPID_ENROLL, &conn) < 0)
+ log_warn_id(id, "Failed to dealloc enrollment flow.");
+
+ log_info_id(id, "Enrolled with %s.", dst);
+
+ info->dir_hash_algo = (enum pol_dir_hash) ipcpi.dir_hash_algo;
+ strcpy(info->name, ipcpi.layer_name);
+
+ return 0;
+
+ fail_start_comp:
+ finalize_components();
+ fail_enroll_boot:
+ connmgr_dealloc(COMPID_ENROLL, &conn);
+ fail_id:
+ return -1;
+}
+
+static int broadcast_ipcp_bootstrap(const struct ipcp_config * conf)
+{
+ assert(conf);
+ assert(conf->type == THIS_TYPE);
+ ((struct ipcp_config *) conf)->layer_info.dir_hash_algo =
+ DIR_HASH_SHA3_256;
+
+ enroll_bootstrap(conf);
+
+ if (initialize_components(conf)) {
+ log_err("Failed to init IPCP components.");
+ goto fail_init;
+ }
+
+ if (start_components()) {
+ log_err("Failed to init IPCP components.");
+ goto fail_start;
+ }
+
+ return 0;
+
+ fail_start:
+ finalize_components();
+ fail_init:
+ return -1;
+}
+
+static int name_check(const uint8_t * dst)
+{
+ uint8_t * buf;
+ size_t len;
+ int ret;
+
+ len = hash_len(ipcpi.dir_hash_algo);
+ buf = malloc(len);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ str_hash(ipcpi.dir_hash_algo, buf, ipcpi.layer_name);
+
+ ret = memcmp(buf, dst, len);
+
+ free(buf);
+
+ return ret;
+}
+
+static int broadcast_ipcp_join(int fd,
+ const uint8_t * dst,
+ qosspec_t qs)
+{
+ struct conn conn;
+ time_t mpl = IPCP_BROADCAST_MPL;
+ buffer_t data = {NULL, 0};
+
+ (void) qs;
+
+ memset(&conn, 0, sizeof(conn));
+
+ conn.flow_info.fd = fd;
+
+ if (name_check(dst) != 0) {
+ log_err("Failed to check name.");
+ return -1;
+ }
+
+ notifier_event(NOTIFY_DT_CONN_ADD, &conn);
+
+ ipcp_flow_alloc_reply(fd, 0, mpl, &data);
+
+ return 0;
+}
+
+int broadcast_ipcp_dealloc(int fd)
+{
+ struct conn conn;
+
+ memset(&conn, 0, sizeof(conn));
+
+ conn.flow_info.fd = fd;
+
+ notifier_event(NOTIFY_DT_CONN_DEL, &conn);
+
+ ipcp_flow_dealloc(fd);
+
+ return 0;
+}
+
+static struct ipcp_ops broadcast_ops = {
+ .ipcp_bootstrap = broadcast_ipcp_bootstrap,
+ .ipcp_enroll = broadcast_ipcp_enroll,
+ .ipcp_connect = connmgr_ipcp_connect,
+ .ipcp_disconnect = connmgr_ipcp_disconnect,
+ .ipcp_reg = NULL,
+ .ipcp_unreg = NULL,
+ .ipcp_query = NULL,
+ .ipcp_flow_alloc = NULL,
+ .ipcp_flow_join = broadcast_ipcp_join,
+ .ipcp_flow_alloc_resp = NULL,
+ .ipcp_flow_dealloc = broadcast_ipcp_dealloc
+};
+
+int main(int argc,
+ char * argv[])
+{
+ if (ipcp_init(argc, argv, &broadcast_ops, THIS_TYPE) < 0) {
+ log_err("Failed to initialize IPCP.");
+ goto fail_init;
+ }
+
+ if (notifier_init()) {
+ log_err("Failed to initialize notifier component.");
+ goto fail_notifier_init;
+ }
+
+ if (connmgr_init()) {
+ log_err("Failed to initialize connection manager.");
+ goto fail_connmgr_init;
+ }
+
+ if (enroll_init()) {
+ log_err("Failed to initialize enrollment component.");
+ goto fail_enroll_init;
+ }
+
+ if (ipcp_start() < 0) {
+ log_err("Failed to boot IPCP.");
+ goto fail_start;
+ }
+
+ ipcp_sigwait();
+
+ if (ipcp_get_state() == IPCP_SHUTDOWN) {
+ stop_components();
+ finalize_components();
+ }
+
+ ipcp_stop();
+
+ enroll_fini();
+
+ connmgr_fini();
+
+ notifier_fini();
+
+ ipcp_fini();
+
+ exit(EXIT_SUCCESS);
+
+ fail_start:
+ enroll_fini();
+ fail_enroll_init:
+ connmgr_fini();
+ fail_connmgr_init:
+ notifier_fini();
+ fail_notifier_init:
+ ipcp_fini();
+ fail_init:
+ exit(EXIT_FAILURE);
+}