/* * Ouroboros - Copyright (C) 2016 - 2017 * * The Flow and Retransmission control component * * 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 "flow-rtx-control" #include <ouroboros/config.h> #include <ouroboros/logs.h> #include <ouroboros/bitmap.h> #include <ouroboros/list.h> #include <ouroboros/ipcp-dev.h> #include <ouroboros/errno.h> #include "frct.h" #include "ipcp.h" #include "dt.h" #include "fa.h" #include <stdlib.h> #include <stdbool.h> #include <pthread.h> #include <string.h> #include <assert.h> enum conn_state { CONN_PENDING = 0, CONN_ESTABLISHED }; struct frct_i { uint32_t cep_id; uint64_t r_address; uint32_t r_cep_id; qoscube_t cube; uint64_t seqno; enum conn_state state; }; struct { struct frct_i ** instances; pthread_mutex_t instances_lock; struct bmp * cep_ids; pthread_mutex_t cep_ids_lock; } frct; static cep_id_t next_cep_id(void) { cep_id_t ret; pthread_mutex_lock(&frct.cep_ids_lock); ret = bmp_allocate(frct.cep_ids); if (!bmp_is_id_valid(frct.cep_ids, ret)) ret = INVALID_CEP_ID; pthread_mutex_unlock(&frct.cep_ids_lock); return ret; } static int release_cep_id(cep_id_t id) { int ret; pthread_mutex_lock(&frct.cep_ids_lock); ret = bmp_release(frct.cep_ids, id); pthread_mutex_unlock(&frct.cep_ids_lock); return ret; } int frct_init() { int i; if (frct_pci_init()) return -1; if (pthread_mutex_init(&frct.cep_ids_lock, NULL)) return -1; frct.cep_ids = bmp_create(IRMD_MAX_FLOWS, (INVALID_CEP_ID + 1)); if (frct.cep_ids == NULL) goto fail_cep_ids_lock; if (pthread_mutex_init(&frct.instances_lock, NULL)) goto fail_bmp; frct.instances = malloc(sizeof(*(frct.instances)) * IRMD_MAX_FLOWS); if (frct.instances == NULL) goto fail_instance_lock; for (i = 0; i < IRMD_MAX_FLOWS; i++) frct.instances[i] = NULL; return 0; fail_instance_lock: pthread_mutex_destroy(&frct.instances_lock); fail_bmp: bmp_destroy(frct.cep_ids); fail_cep_ids_lock: pthread_mutex_destroy(&frct.cep_ids_lock); return -1; } int frct_fini() { size_t len = IRMD_MAX_FLOWS; pthread_mutex_destroy(&frct.instances_lock); freepp(struct frct_i, frct.instances, len); bmp_destroy(frct.cep_ids); pthread_mutex_destroy(&frct.cep_ids_lock); return 0; } cep_id_t frct_i_create(uint64_t address, qoscube_t qc) { struct frct_i * instance; cep_id_t id; pthread_mutex_lock(&frct.instances_lock); instance = malloc(sizeof(*instance)); if (instance == NULL) return INVALID_CEP_ID; id = next_cep_id(); if (id == INVALID_CEP_ID) { free(instance); return INVALID_CEP_ID; } instance->r_address = address; instance->cep_id = id; instance->state = CONN_PENDING; instance->seqno = 0; instance->cube = qc; frct.instances[id] = instance; pthread_mutex_unlock(&frct.instances_lock); return id; } int frct_i_destroy(cep_id_t cep_id) { struct frct_i * instance; pthread_mutex_lock(&frct.instances_lock); instance = frct.instances[cep_id]; if (instance == NULL) { pthread_mutex_unlock(&frct.instances_lock); log_err("Invalid instance."); return -1; } frct.instances[cep_id] = NULL; release_cep_id(instance->cep_id); free(instance); pthread_mutex_unlock(&frct.instances_lock); return 0; } int frct_i_set_id(cep_id_t cep_id, cep_id_t r_cep_id) { struct frct_i * instance; pthread_mutex_lock(&frct.instances_lock); instance = frct.instances[cep_id]; if (instance == NULL) { pthread_mutex_unlock(&frct.instances_lock); log_err("Invalid instance."); return -1; } instance->r_cep_id = r_cep_id; instance->state = CONN_ESTABLISHED; pthread_mutex_unlock(&frct.instances_lock); return 0; } cep_id_t frct_i_get_id(cep_id_t cep_id) { struct frct_i * instance; cep_id_t r_cep_id; pthread_mutex_lock(&frct.instances_lock); instance = frct.instances[cep_id]; if (instance == NULL) { pthread_mutex_unlock(&frct.instances_lock); return INVALID_CEP_ID; } r_cep_id = instance->r_cep_id; pthread_mutex_unlock(&frct.instances_lock); return r_cep_id; } uint64_t frct_i_get_addr(cep_id_t cep_id) { struct frct_i * instance; uint64_t r_addr; pthread_mutex_lock(&frct.instances_lock); instance = frct.instances[cep_id]; if (instance == NULL) { pthread_mutex_unlock(&frct.instances_lock); return INVALID_ADDR; } r_addr = instance->r_address; pthread_mutex_unlock(&frct.instances_lock); return r_addr; } int frct_post_sdu(struct shm_du_buff * sdb) { struct frct_pci frct_pci; struct frct_i * instance; assert(sdb); memset(&frct_pci, 0, sizeof(frct_pci)); frct_pci_des(sdb, &frct_pci); /* Known cep-ids are delivered to FA (minimal DTP) */ pthread_mutex_lock(&frct.instances_lock); instance = frct.instances[frct_pci.dst_cep_id]; if (instance == NULL) { pthread_mutex_unlock(&frct.instances_lock); log_err("Invalid instance."); return -1; } if (instance->state != CONN_ESTABLISHED) { pthread_mutex_unlock(&frct.instances_lock); log_err("Connection is not established."); return -1; } pthread_mutex_unlock(&frct.instances_lock); if (fa_post_sdu_user(frct_pci.dst_cep_id, sdb)) return -1; return 0; } int frct_i_write_sdu(cep_id_t id, struct shm_du_buff * sdb) { struct frct_i * instance; struct frct_pci frct_pci; assert(sdb); pthread_mutex_lock(&frct.instances_lock); instance = frct.instances[id]; if (instance == NULL) { pthread_mutex_unlock(&frct.instances_lock); log_err("Invalid instance."); return -1; } if (instance->state != CONN_ESTABLISHED) { pthread_mutex_unlock(&frct.instances_lock); log_err("Connection is not established."); return -1; } frct_pci.dst_cep_id = instance->r_cep_id; frct_pci.seqno = (instance->seqno)++; if (frct_pci_ser(sdb, &frct_pci)) { pthread_mutex_unlock(&frct.instances_lock); log_err("Failed to serialize."); return -1; } if (dt_write_sdu(instance->r_address, instance->cube, PDU_TYPE_FRCT, sdb)) { pthread_mutex_unlock(&frct.instances_lock); log_err("Failed to hand SDU to DT."); return -1; } pthread_mutex_unlock(&frct.instances_lock); return 0; }