diff options
Diffstat (limited to 'src/ipcpd/broadcast')
| -rw-r--r-- | src/ipcpd/broadcast/CMakeLists.txt | 35 | ||||
| -rw-r--r-- | src/ipcpd/broadcast/connmgr.c | 35 | ||||
| -rw-r--r-- | src/ipcpd/broadcast/dt.c | 322 | ||||
| -rw-r--r-- | src/ipcpd/broadcast/dt.h | 27 | ||||
| -rw-r--r-- | src/ipcpd/broadcast/main.c | 321 |
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); +} |
