/* * Ouroboros - Copyright (C) 2016 - 2017 * * Handles AE connections * * Dimitri Staessens <dimitri.staessens@ugent.be> * Sander Vrijders <sander.vrijders@ugent.be> * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define OUROBOROS_PREFIX "normal-ipcp" #include <ouroboros/config.h> #include <ouroboros/logs.h> #include <ouroboros/dev.h> #include <ouroboros/cacep.h> #include <ouroboros/cdap.h> #include <ouroboros/errno.h> #include "ae.h" #include "connmgr.h" #include "enroll.h" #include "fmgr.h" #include "frct.h" #include "ipcp.h" #include "ribmgr.h" #include <pthread.h> #include <string.h> #include <stdlib.h> #include <assert.h> struct ae_conn { struct list_head next; struct conn conn; }; struct ae { struct list_head next; struct conn_info info; struct list_head conn_list; pthread_cond_t conn_cond; pthread_mutex_t conn_lock; }; struct { pthread_t acceptor; struct list_head aes; pthread_mutex_t aes_lock; } connmgr; static int add_ae_conn(struct ae * ae, int fd, qosspec_t qs, struct conn_info * rcv_info) { struct ae_conn * ae_conn = NULL; ae_conn = malloc(sizeof(*ae_conn)); if (ae_conn == NULL) { log_err("Not enough memory."); return -1; } ae_conn->conn.conn_info = *rcv_info; ae_conn->conn.flow_info.fd = fd; ae_conn->conn.flow_info.qs = qs; list_head_init(&ae_conn->next); pthread_mutex_lock(&ae->conn_lock); list_add(&ae_conn->next, &ae->conn_list); pthread_cond_signal(&ae->conn_cond); pthread_mutex_unlock(&ae->conn_lock); return 0; } static struct ae * find_ae_by_name(char * name) { struct list_head * p = NULL; list_for_each(p, &connmgr.aes) { struct ae * ae = list_entry(p, struct ae, next); if (strcmp(ae->info.ae_name, name) == 0) return ae; } return NULL; } static void * flow_acceptor(void * o) { int fd; qosspec_t qs; struct conn_info rcv_info; struct conn_info fail_info; struct ae * ae = NULL; (void) o; memset(&fail_info, 0, sizeof(fail_info)); while (true) { if (ipcp_get_state() != IPCP_OPERATIONAL) { log_info("Shutting down flow acceptor."); return 0; } fd = flow_accept(&qs, NULL); if (fd < 0) { if (fd != -EIRMD) log_warn("Flow accept failed: %d", fd); continue; } if (cacep_rcv(fd, &rcv_info)) { log_err("Error establishing application connection."); flow_dealloc(fd); continue; } pthread_mutex_lock(&connmgr.aes_lock); ae = find_ae_by_name(rcv_info.ae_name); pthread_mutex_unlock(&connmgr.aes_lock); if (ae != NULL) { if (cacep_snd(fd, &ae->info)) { log_err("Failed to respond to req."); flow_dealloc(fd); continue; } if (add_ae_conn(ae, fd, qs, &rcv_info)) { log_err("Failed to add ae conn."); flow_dealloc(fd); continue; } } else { cacep_snd(fd, &fail_info); flow_dealloc(fd); } } return (void *) 0; } int connmgr_init(void) { list_head_init(&connmgr.aes); if (pthread_mutex_init(&connmgr.aes_lock, NULL)) return -1; return 0; } int connmgr_start(void) { pthread_create(&connmgr.acceptor, NULL, flow_acceptor, NULL); return 0; } void connmgr_stop(void) { pthread_cancel(connmgr.acceptor); pthread_join(connmgr.acceptor, NULL); } static void destroy_ae(struct ae * ae) { struct list_head * p = NULL; struct list_head * h = NULL; pthread_mutex_lock(&ae->conn_lock); list_for_each_safe(p, h, &ae->conn_list) { struct ae_conn * e = list_entry(p, struct ae_conn, next); list_del(&e->next); free(e); } pthread_mutex_unlock(&ae->conn_lock); pthread_cond_destroy(&ae->conn_cond); pthread_mutex_destroy(&ae->conn_lock); free(ae); } void connmgr_fini(void) { struct list_head * p = NULL; struct list_head * n = NULL; pthread_mutex_lock(&connmgr.aes_lock); list_for_each_safe(p, n, &connmgr.aes) { struct ae * e = list_entry(p, struct ae, next); list_del(&e->next); destroy_ae(e); } pthread_mutex_unlock(&connmgr.aes_lock); pthread_mutex_destroy(&connmgr.aes_lock); } struct ae * connmgr_ae_create(struct conn_info info) { struct ae * ae; ae = malloc(sizeof(*ae)); if (ae == NULL) return NULL; list_head_init(&ae->next); list_head_init(&ae->conn_list); ae->info = info; if (pthread_mutex_init(&ae->conn_lock, NULL)) { free(ae); return NULL; } if (pthread_cond_init(&ae->conn_cond, NULL)) { pthread_mutex_destroy(&ae->conn_lock); free(ae); return NULL; } pthread_mutex_lock(&connmgr.aes_lock); list_add(&ae->next, &connmgr.aes); pthread_mutex_unlock(&connmgr.aes_lock); return ae; } void connmgr_ae_destroy(struct ae * ae) { assert(ae); pthread_mutex_lock(&connmgr.aes_lock); list_del(&ae->next); destroy_ae(ae); pthread_mutex_unlock(&connmgr.aes_lock); } int connmgr_alloc(struct ae * ae, const char * dst_name, qosspec_t * qs, struct conn * conn) { assert(ae); assert(dst_name); assert(conn); memset(&conn->conn_info, 0, sizeof(conn->conn_info)); conn->flow_info.fd = flow_alloc(dst_name, qs, NULL); if (conn->flow_info.fd < 0) { log_err("Failed to allocate flow to %s.", dst_name); return -1; } if (qs != NULL) conn->flow_info.qs = *qs; else memset(&conn->flow_info.qs, 0, sizeof(conn->flow_info.qs)); if (cacep_snd(conn->flow_info.fd, &ae->info)) { log_err("Failed to create application connection."); flow_dealloc(conn->flow_info.fd); return -1; } if (cacep_rcv(conn->flow_info.fd, &conn->conn_info)) { log_err("Failed to connect to application."); flow_dealloc(conn->flow_info.fd); return -1; } if (strcmp(ae->info.protocol, conn->conn_info.protocol) || ae->info.pref_version != conn->conn_info.pref_version || ae->info.pref_syntax != conn->conn_info.pref_syntax) { flow_dealloc(conn->flow_info.fd); return -1; } return 0; } int connmgr_wait(struct ae * ae, struct conn * conn) { struct ae_conn * ae_conn = NULL; assert(ae); assert(conn); pthread_mutex_lock(&ae->conn_lock); pthread_cleanup_push((void(*)(void *))pthread_mutex_unlock, (void *) &ae->conn_lock); while (list_is_empty(&ae->conn_list)) pthread_cond_wait(&ae->conn_cond, &ae->conn_lock); pthread_cleanup_pop(false); ae_conn = list_first_entry((&ae->conn_list), struct ae_conn, next); if (ae_conn == NULL) { pthread_mutex_unlock(&ae->conn_lock); return -1; } *conn = ae_conn->conn; list_del(&ae_conn->next); free(ae_conn); pthread_mutex_unlock(&ae->conn_lock); return 0; }