summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ouroboros/cacep.h47
-rw-r--r--src/ipcpd/ipcp.c16
-rw-r--r--src/ipcpd/ipcp.h3
-rw-r--r--src/ipcpd/local/main.c8
-rw-r--r--src/ipcpd/normal/CMakeLists.txt1
-rw-r--r--src/ipcpd/normal/fmgr.c197
-rw-r--r--src/ipcpd/normal/fmgr.h10
-rw-r--r--src/ipcpd/normal/gam.c339
-rw-r--r--src/ipcpd/normal/gam.h44
-rw-r--r--src/ipcpd/normal/main.c27
-rw-r--r--src/ipcpd/normal/ribmgr.c6
-rw-r--r--src/ipcpd/shim-eth-llc/main.c8
-rw-r--r--src/ipcpd/shim-udp/main.c8
-rw-r--r--src/irmd/ipcp.c12
-rw-r--r--src/irmd/ipcp.h6
-rw-r--r--src/irmd/main.c2
-rw-r--r--src/lib/CMakeLists.txt11
-rw-r--r--src/lib/cacep.c170
-rw-r--r--src/lib/cacep.proto29
-rw-r--r--src/tools/irm/irm_ipcp_bootstrap.c2
-rw-r--r--src/tools/irm/irm_ipcp_create.c3
-rw-r--r--src/tools/irm/irm_ipcp_enroll.c1
22 files changed, 808 insertions, 142 deletions
diff --git a/include/ouroboros/cacep.h b/include/ouroboros/cacep.h
new file mode 100644
index 00000000..7b22b4a2
--- /dev/null
+++ b/include/ouroboros/cacep.h
@@ -0,0 +1,47 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2017
+ *
+ * The Common Application Connection Establishment Phase
+ *
+ * Sander Vrijders <sander.vrijders@intec.ugent.be>
+ * Dimitri Staessens <dimitri.staessens@intec.ugent.be>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OUROBOROS_CACEP_H
+#define OUROBOROS_CACEP_H
+
+#include <stdint.h>
+#include <unistd.h>
+
+struct cacep;
+
+struct cacep_info {
+ char * name;
+ uint64_t addr;
+};
+
+struct cacep * cacep_create(int fd,
+ char * name,
+ uint64_t address);
+
+int cacep_destroy(struct cacep * instance);
+
+struct cacep_info * cacep_auth(struct cacep * instance);
+
+struct cacep_info * cacep_auth_wait(struct cacep * instance);
+
+#endif /* OUROBOROS_CACEP_H */
diff --git a/src/ipcpd/ipcp.c b/src/ipcpd/ipcp.c
index 2e4c3fca..96f00dc0 100644
--- a/src/ipcpd/ipcp.c
+++ b/src/ipcpd/ipcp.c
@@ -440,22 +440,26 @@ int ipcp_parse_arg(int argc, char * argv[])
if (atoi(argv[1]) == 0)
return -1;
- if (argv[2] == NULL)
+ ipcpi.irmd_api = atoi(argv[1]);
+
+ /* argument 2: IPCP name */
+ ipcpi.name = argv[2];
+
+ /* argument 3: logfile name (if any) */
+ if (argv[3] == NULL)
return 0;
len += strlen(INSTALL_PREFIX);
len += strlen(LOG_DIR);
- len += strlen(argv[2]);
+ len += strlen(argv[3]);
log_file = malloc(len + 1);
- if (log_file == NULL) {
- LOG_ERR("Failed to malloc");
+ if (log_file == NULL)
return -1;
- }
strcpy(log_file, INSTALL_PREFIX);
strcat(log_file, LOG_DIR);
- strcat(log_file, argv[2]);
+ strcat(log_file, argv[3]);
log_file[len] = '\0';
if (set_logfile(log_file))
diff --git a/src/ipcpd/ipcp.h b/src/ipcpd/ipcp.h
index ae5a56da..a75186ba 100644
--- a/src/ipcpd/ipcp.h
+++ b/src/ipcpd/ipcp.h
@@ -38,6 +38,9 @@ enum ipcp_state {
};
struct ipcp {
+ int irmd_api;
+ char * name;
+
struct ipcp_data * data;
struct ipcp_ops * ops;
int irmd_fd;
diff --git a/src/ipcpd/local/main.c b/src/ipcpd/local/main.c
index 58949aea..5117f59d 100644
--- a/src/ipcpd/local/main.c
+++ b/src/ipcpd/local/main.c
@@ -41,9 +41,6 @@
#define EVENT_WAIT_TIMEOUT 100 /* us */
#define THIS_TYPE IPCP_LOCAL
-/* global for trapping signal */
-int irmd_api;
-
struct {
int in_out[IRMD_MAX_FLOWS];
flow_set_t * flows;
@@ -127,7 +124,7 @@ void ipcp_sig_handler(int sig, siginfo_t * info, void * c)
case SIGTERM:
case SIGHUP:
case SIGQUIT:
- if (info->si_pid == irmd_api) {
+ if (info->si_pid == ipcpi.irmd_api) {
pthread_rwlock_wrlock(&ipcpi.state_lock);
if (ipcp_get_state() == IPCP_INIT)
@@ -349,9 +346,6 @@ int main(int argc, char * argv[])
exit(EXIT_FAILURE);
}
- /* store the process id of the irmd */
- irmd_api = atoi(argv[1]);
-
/* init sig_act */
memset(&sig_act, 0, sizeof(sig_act));
diff --git a/src/ipcpd/normal/CMakeLists.txt b/src/ipcpd/normal/CMakeLists.txt
index bdcb78ae..43059c3e 100644
--- a/src/ipcpd/normal/CMakeLists.txt
+++ b/src/ipcpd/normal/CMakeLists.txt
@@ -29,6 +29,7 @@ set(SOURCE_FILES
dir.c
fmgr.c
frct.c
+ gam.c
main.c
pathname.c
pff.c
diff --git a/src/ipcpd/normal/fmgr.c b/src/ipcpd/normal/fmgr.c
index a419e9f5..d839cf1b 100644
--- a/src/ipcpd/normal/fmgr.c
+++ b/src/ipcpd/normal/fmgr.c
@@ -28,6 +28,7 @@
#include <ouroboros/ipcp-dev.h>
#include <ouroboros/fqueue.h>
#include <ouroboros/errno.h>
+#include <ouroboros/cacep.h>
#include <stdlib.h>
#include <stdbool.h>
@@ -42,15 +43,24 @@
#include "dir.h"
#include "pathname.h"
#include "ro.h"
+#include "gam.h"
#include "flow_alloc.pb-c.h"
typedef FlowAllocMsg flow_alloc_msg_t;
#define FD_UPDATE_TIMEOUT 100000 /* nanoseconds */
+struct nm1_flow {
+ struct list_head next;
+ int fd;
+ qosspec_t qs;
+ struct cacep_info * info;
+};
+
struct {
flow_set_t * nm1_set[QOS_CUBE_MAX];
fqueue_t * nm1_fqs[QOS_CUBE_MAX];
+ struct list_head nm1_flows;
pthread_rwlock_t nm1_flows_lock;
flow_set_t * np1_set[QOS_CUBE_MAX];
@@ -60,21 +70,23 @@ struct {
cep_id_t np1_fd_to_cep_id[AP_MAX_FLOWS];
int np1_cep_id_to_fd[IPCPD_MAX_CONNS];
- pthread_t nm1_sdu_reader;
pthread_t np1_sdu_reader;
+ pthread_t nm1_sdu_reader;
+ pthread_t nm1_flow_wait;
/* FIXME: Replace with PFF */
int fd;
+
+ struct gam * gam;
} fmgr;
static void * fmgr_np1_sdu_reader(void * o)
{
struct shm_du_buff * sdb;
- struct timespec timeout = {0, FD_UPDATE_TIMEOUT};
- int fd;
-
- int i = 0;
- int ret;
+ struct timespec timeout = {0, FD_UPDATE_TIMEOUT};
+ int fd;
+ int i = 0;
+ int ret;
(void) o;
@@ -118,12 +130,12 @@ static void * fmgr_np1_sdu_reader(void * o)
void * fmgr_nm1_sdu_reader(void * o)
{
- struct timespec timeout = {0, FD_UPDATE_TIMEOUT};
+ struct timespec timeout = {0, FD_UPDATE_TIMEOUT};
struct shm_du_buff * sdb;
- struct pci * pci;
- int fd;
- int i = 0;
- int ret;
+ struct pci * pci;
+ int fd;
+ int i = 0;
+ int ret;
(void) o;
@@ -202,6 +214,49 @@ void * fmgr_nm1_sdu_reader(void * o)
return (void *) 0;
}
+static void * fmgr_nm1_flow_wait(void * o)
+{
+ qoscube_t cube;
+ struct cacep_info * info;
+ int fd;
+ qosspec_t qs;
+ struct nm1_flow * flow;
+
+ (void) o;
+
+ while (true) {
+ if (gam_flow_wait(fmgr.gam, &fd, &info, &qs)) {
+ LOG_ERR("Failed to get next flow descriptor.");
+ continue;;
+ }
+
+ ipcp_flow_get_qoscube(fd, &cube);
+ flow_set_add(fmgr.nm1_set[cube], fd);
+
+ /* FIXME: Temporary, until we have a PFF */
+ fmgr.fd = fd;
+
+ pthread_rwlock_wrlock(&fmgr.nm1_flows_lock);
+ flow = malloc(sizeof(*flow));
+ if (flow == NULL) {
+ free(info);
+ pthread_rwlock_unlock(&fmgr.nm1_flows_lock);
+ continue;
+ }
+
+ flow->info = info;
+ flow->fd = fd;
+ flow->qs = qs;
+
+ INIT_LIST_HEAD(&flow->next);
+ list_add(&flow->next, &fmgr.nm1_flows);
+
+ pthread_rwlock_unlock(&fmgr.nm1_flows_lock);
+ }
+
+ return (void *) 0;
+}
+
static void fmgr_destroy_flows(void)
{
int i;
@@ -224,9 +279,6 @@ int fmgr_init()
for (i = 0; i < IPCPD_MAX_CONNS; ++i)
fmgr.np1_cep_id_to_fd[i] = -1;
- pthread_rwlock_init(&fmgr.nm1_flows_lock, NULL);
- pthread_rwlock_init(&fmgr.np1_flows_lock, NULL);
-
for (i = 0; i < QOS_CUBE_MAX; ++i) {
fmgr.np1_set[i] = flow_set_create();
if (fmgr.np1_set[i] == NULL) {
@@ -253,29 +305,55 @@ int fmgr_init()
}
}
+ fmgr.gam = gam_create(DT_AE);
+ if (fmgr.gam == NULL) {
+ LOG_ERR("Failed to create graph adjacency manager.");
+ fmgr_destroy_flows();
+ return -1;
+ }
+
+ INIT_LIST_HEAD(&fmgr.nm1_flows);
+
+ pthread_rwlock_init(&fmgr.nm1_flows_lock, NULL);
+ pthread_rwlock_init(&fmgr.np1_flows_lock, NULL);
+
pthread_create(&fmgr.np1_sdu_reader, NULL, fmgr_np1_sdu_reader, NULL);
pthread_create(&fmgr.nm1_sdu_reader, NULL, fmgr_nm1_sdu_reader, NULL);
+ pthread_create(&fmgr.nm1_flow_wait, NULL, fmgr_nm1_flow_wait, NULL);
return 0;
}
int fmgr_fini()
{
- int i;
- int j;
+ struct list_head * pos = NULL;
+ struct list_head * n = NULL;
+ qoscube_t cube;
pthread_cancel(fmgr.np1_sdu_reader);
pthread_cancel(fmgr.nm1_sdu_reader);
+ pthread_cancel(fmgr.nm1_flow_wait);
pthread_join(fmgr.np1_sdu_reader, NULL);
pthread_join(fmgr.nm1_sdu_reader, NULL);
+ pthread_join(fmgr.nm1_flow_wait, NULL);
- for (i = 0; i < AP_MAX_FLOWS; ++i)
- for (j = 0; j < QOS_CUBE_MAX; ++j)
- if (flow_set_has(fmgr.nm1_set[j], i)) {
- flow_dealloc(i);
- flow_set_del(fmgr.nm1_set[j], i);
- }
+ gam_destroy(fmgr.gam);
+
+ pthread_rwlock_wrlock(&fmgr.nm1_flows_lock);
+
+ list_for_each_safe(pos, n, &fmgr.nm1_flows) {
+ struct nm1_flow * flow =
+ list_entry(pos, struct nm1_flow, next);
+ list_del(&flow->next);
+ flow_dealloc(flow->fd);
+ ipcp_flow_get_qoscube(flow->fd, &cube);
+ flow_set_del(fmgr.nm1_set[cube], flow->fd);
+ free(flow->info);
+ free(flow);
+ }
+
+ pthread_rwlock_unlock(&fmgr.nm1_flows_lock);
pthread_rwlock_destroy(&fmgr.nm1_flows_lock);
pthread_rwlock_destroy(&fmgr.np1_flows_lock);
@@ -290,12 +368,12 @@ int fmgr_np1_alloc(int fd,
char * src_ae_name,
qoscube_t cube)
{
- cep_id_t cep_id;
- buffer_t buf;
+ cep_id_t cep_id;
+ buffer_t buf;
flow_alloc_msg_t msg = FLOW_ALLOC_MSG__INIT;
- char * path;
- uint8_t * ro_data;
- uint64_t addr;
+ char * path;
+ uint8_t * ro_data;
+ uint64_t addr;
path = pathname_create(RO_DIR);
if (path == NULL)
@@ -359,9 +437,9 @@ int fmgr_np1_alloc(int fd,
static int np1_flow_dealloc(int fd)
{
flow_alloc_msg_t msg = FLOW_ALLOC_MSG__INIT;
- buffer_t buf;
- int ret;
- qoscube_t cube;
+ buffer_t buf;
+ int ret;
+ qoscube_t cube;
ipcp_flow_get_qoscube(fd, &cube);
flow_set_del(fmgr.np1_set[cube], fd);
@@ -388,10 +466,11 @@ static int np1_flow_dealloc(int fd)
return ret;
}
-int fmgr_np1_alloc_resp(int fd, int response)
+int fmgr_np1_alloc_resp(int fd,
+ int response)
{
flow_alloc_msg_t msg = FLOW_ALLOC_MSG__INIT;
- buffer_t buf;
+ buffer_t buf;
msg.code = FLOW_ALLOC_CODE__FLOW_REPLY;
msg.response = response;
@@ -443,7 +522,8 @@ int fmgr_np1_dealloc(int fd)
return ret;
}
-int fmgr_np1_post_buf(cep_id_t cep_id, buffer_t * buf)
+int fmgr_np1_post_buf(cep_id_t cep_id,
+ buffer_t * buf)
{
int ret = 0;
int fd;
@@ -512,7 +592,8 @@ int fmgr_np1_post_buf(cep_id_t cep_id, buffer_t * buf)
return ret;
}
-int fmgr_np1_post_sdu(cep_id_t cep_id, struct shm_du_buff * sdb)
+int fmgr_np1_post_sdu(cep_id_t cep_id,
+ struct shm_du_buff * sdb)
{
int fd;
@@ -530,52 +611,21 @@ int fmgr_np1_post_sdu(cep_id_t cep_id, struct shm_du_buff * sdb)
return 0;
}
-/* FIXME: do this in a topologymanager instance */
-int fmgr_nm1_add_flow(int fd)
+int fmgr_nm1_flow_arr(int fd,
+ qosspec_t qs)
{
- qoscube_t qos;
+ assert(fmgr.gam);
- if (flow_alloc_resp(fd, 0) < 0) {
- LOG_ERR("Could not respond to new flow.");
+ if (gam_flow_arr(fmgr.gam, fd, qs)) {
+ LOG_ERR("Failed to hand to connectivy manager.");
return -1;
}
- ipcp_flow_get_qoscube(fd, &qos);
- flow_set_add(fmgr.nm1_set[qos], fd);
-
- /* FIXME: Temporary, until we have a PFF */
- fmgr.fd = fd;
-
- return 0;
-}
-
-int fmgr_nm1_dt_flow(char * dst_name, qoscube_t qos)
-{
- int fd;
- int result;
-
- /* FIXME: Map qos cube on correct QoS. */
- fd = flow_alloc(dst_name, DT_AE, NULL);
- if (fd < 0) {
- LOG_ERR("Failed to allocate flow to %s.", dst_name);
- return -1;
- }
-
- result = flow_alloc_res(fd);
- if (result < 0) {
- LOG_ERR("Allocate flow to %s result %d.", dst_name, result);
- return -1;
- }
-
- flow_set_add(fmgr.nm1_set[qos], fd);
-
- /* FIXME: Temporary, until we have a PFF */
- fmgr.fd = fd;
-
return 0;
}
-int fmgr_nm1_write_sdu(struct pci * pci, struct shm_du_buff * sdb)
+int fmgr_nm1_write_sdu(struct pci * pci,
+ struct shm_du_buff * sdb)
{
if (pci == NULL || sdb == NULL)
return -1;
@@ -595,7 +645,8 @@ int fmgr_nm1_write_sdu(struct pci * pci, struct shm_du_buff * sdb)
return 0;
}
-int fmgr_nm1_write_buf(struct pci * pci, buffer_t * buf)
+int fmgr_nm1_write_buf(struct pci * pci,
+ buffer_t * buf)
{
buffer_t * buffer;
diff --git a/src/ipcpd/normal/fmgr.h b/src/ipcpd/normal/fmgr.h
index 85731081..ae5c8ea8 100644
--- a/src/ipcpd/normal/fmgr.h
+++ b/src/ipcpd/normal/fmgr.h
@@ -23,6 +23,7 @@
#define OUROBOROS_IPCPD_NORMAL_FMGR_H
#include <ouroboros/shared.h>
+#include <ouroboros/qos.h>
#include "ae.h"
#include "frct.h"
@@ -47,15 +48,14 @@ int fmgr_np1_post_buf(cep_id_t id,
int fmgr_np1_post_sdu(cep_id_t id,
struct shm_du_buff * sdb);
-int fmgr_nm1_add_flow(int fd);
-
-int fmgr_nm1_dt_flow(char * dst_name,
- qoscube_t qos);
-
int fmgr_nm1_write_sdu(struct pci * pci,
struct shm_du_buff * sdb);
int fmgr_nm1_write_buf(struct pci * pci,
buffer_t * buf);
+int fmgr_nm1_flow_arr(int fd,
+ qosspec_t qs);
+
+
#endif /* OUROBOROS_IPCPD_NORMAL_FMGR_H */
diff --git a/src/ipcpd/normal/gam.c b/src/ipcpd/normal/gam.c
new file mode 100644
index 00000000..a749563d
--- /dev/null
+++ b/src/ipcpd/normal/gam.c
@@ -0,0 +1,339 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2017
+ *
+ * Graph adjacency manager for IPC Process components
+ *
+ * Dimitri Staeesens <dimitri.staessens@intec.ugent.be>
+ * Sander Vrijders <sander.vrijders@intec.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 "graph-adjacency-manager"
+
+#include <ouroboros/config.h>
+#include <ouroboros/dev.h>
+#include <ouroboros/logs.h>
+#include <ouroboros/cacep.h>
+#include <ouroboros/list.h>
+#include <ouroboros/errno.h>
+
+#include "ribmgr.h"
+#include "ipcp.h"
+#include "ro.h"
+#include "pathname.h"
+#include "gam.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+
+#define RO_DIR "neighbors"
+
+struct ga {
+ struct list_head next;
+
+ qosspec_t qs;
+ int fd;
+ struct cacep_info * info;
+};
+
+struct gam {
+ struct list_head gas;
+ pthread_mutex_t gas_lock;
+ pthread_cond_t gas_cond;
+
+ char * ae_name;
+
+ /* FIXME: Keep a list of known members */
+
+ pthread_t allocator;
+};
+
+static void * allocator(void * o)
+{
+ qosspec_t qs;
+ ssize_t len;
+ char ** children;
+ struct gam * instance;
+ int i;
+ char * ro_name;
+
+ instance = (struct gam *) o;
+
+ qs.delay = 0;
+ qs.jitter = 0;
+
+ ro_name = pathname_create(RO_DIR);
+ if (ro_name == NULL)
+ return (void *) -1;
+
+ len = ro_children(ro_name, &children);
+ if (len > 0) {
+ for (i = 0; i < len; i++) {
+ if (strcmp(children[i], ipcpi.name) == 0)
+ continue;
+ gam_flow_alloc(instance, children[i], qs);
+ }
+ }
+
+ pathname_destroy(ro_name);
+
+ return (void *) 0;
+}
+
+struct gam * gam_create(char * ae_name)
+{
+ struct gam * tmp;
+ struct ro_attr attr;
+ char * ro_name;
+
+ tmp = malloc(sizeof(*tmp));
+ if (tmp == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&tmp->gas);
+
+ tmp->ae_name = strdup(ae_name);
+ if (tmp->ae_name == NULL) {
+ free(tmp);
+ return NULL;
+ }
+
+ if (pthread_mutex_init(&tmp->gas_lock, NULL)) {
+ free(tmp->ae_name);
+ free(tmp);
+ return NULL;
+ }
+
+ if (pthread_cond_init(&tmp->gas_cond, NULL)) {
+ pthread_mutex_destroy(&tmp->gas_lock);
+ free(tmp->ae_name);
+ free(tmp);
+ return NULL;
+ }
+
+ ro_attr_init(&attr);
+ attr.enrol_sync = true;
+ attr.recv_set = ALL_MEMBERS;
+
+ ro_name = pathname_create(RO_DIR);
+ if (ro_name == NULL) {
+ pthread_mutex_destroy(&tmp->gas_lock);
+ free(tmp->ae_name);
+ free(tmp);
+ return NULL;
+ }
+
+ if (!ro_exists(RO_DIR)) {
+ if (ro_create(ro_name, &attr, NULL, 0)) {
+ pathname_destroy(ro_name);
+ pthread_mutex_destroy(&tmp->gas_lock);
+ free(tmp->ae_name);
+ free(tmp);
+ return NULL;
+ }
+ }
+
+ ro_name = pathname_append(ro_name, ipcpi.name);
+ if (ro_name == NULL) {
+ pathname_destroy(ro_name);
+ pthread_mutex_destroy(&tmp->gas_lock);
+ free(tmp->ae_name);
+ free(tmp);
+ return NULL;
+ }
+
+ if (ro_create(ro_name, &attr, NULL, 0)) {
+ pathname_destroy(ro_name);
+ pthread_mutex_destroy(&tmp->gas_lock);
+ free(tmp->ae_name);
+ free(tmp);
+ return NULL;
+ }
+ pathname_destroy(ro_name);
+
+ if (pthread_create(&tmp->allocator, NULL, allocator, (void *) tmp)) {
+ pthread_cond_destroy(&tmp->gas_cond);
+ pthread_mutex_destroy(&tmp->gas_lock);
+ free(tmp->ae_name);
+ free(tmp);
+ return NULL;
+ }
+
+ return tmp;
+}
+
+void gam_destroy(struct gam * instance)
+{
+ struct list_head * p = NULL;
+ struct list_head * n = NULL;
+
+ assert(instance);
+
+ pthread_cancel(instance->allocator);
+ pthread_join(instance->allocator, NULL);
+
+ pthread_mutex_destroy(&instance->gas_lock);
+ pthread_cond_destroy(&instance->gas_cond);
+
+ list_for_each_safe(p, n, &instance->gas) {
+ struct ga * e = list_entry(p, struct ga, next);
+ list_del(&e->next);
+ free(e->info);
+ free(e);
+ }
+
+ free(instance->ae_name);
+ free(instance);
+}
+
+static int add_ga(struct gam * instance,
+ int fd,
+ qosspec_t qs,
+ struct cacep_info * info)
+{
+ struct ga * ga;
+
+ ga = malloc(sizeof(*ga));
+ if (ga == NULL)
+ return -ENOMEM;
+
+ ga->fd = fd;
+ ga->info = info;
+ ga->qs = qs;
+
+ INIT_LIST_HEAD(&ga->next);
+
+ pthread_mutex_lock(&instance->gas_lock);
+ list_add(&ga->next, &instance->gas);
+ pthread_cond_signal(&instance->gas_cond);
+ pthread_mutex_unlock(&instance->gas_lock);
+
+ return 0;
+}
+
+int gam_flow_arr(struct gam * instance,
+ int fd,
+ qosspec_t qs)
+{
+ struct cacep * cacep;
+ struct cacep_info * info;
+
+ if (flow_alloc_resp(fd, 0) < 0) {
+ LOG_ERR("Could not respond to new flow.");
+ return -1;
+ }
+
+ cacep = cacep_create(fd, ipcpi.name, ribmgr_address());
+ if (cacep == NULL) {
+ LOG_ERR("Failed to create CACEP instance.");
+ return -1;
+ }
+
+ info = cacep_auth_wait(cacep);
+ if (info == NULL) {
+ LOG_ERR("Other side failed to authenticate.");
+ cacep_destroy(cacep);
+ return -1;
+ }
+ cacep_destroy(cacep);
+
+ if (add_ga(instance, fd, qs, info)) {
+ LOG_ERR("Failed to add ga to graph adjacency manager list.");
+ free(info);
+ return -1;
+ }
+
+ return 0;
+}
+
+int gam_flow_alloc(struct gam * instance,
+ char * dst_name,
+ qosspec_t qs)
+{
+ struct cacep * cacep;
+ struct cacep_info * info;
+ int fd;
+
+ fd = flow_alloc(dst_name, instance->ae_name, NULL);
+ if (fd < 0) {
+ LOG_ERR("Failed to allocate flow to %s.", dst_name);
+ return -1;
+ }
+
+ if (flow_alloc_res(fd)) {
+ LOG_ERR("Flow allocation to %s failed.", dst_name);
+ flow_dealloc(fd);
+ return -1;
+ }
+
+ cacep = cacep_create(fd, ipcpi.name, ribmgr_address());
+ if (cacep == NULL) {
+ LOG_ERR("Failed to create CACEP instance.");
+ return -1;
+ }
+
+ info = cacep_auth(cacep);
+ if (info == NULL) {
+ LOG_ERR("Failed to authenticate.");
+ cacep_destroy(cacep);
+ return -1;
+ }
+ cacep_destroy(cacep);
+
+ if (add_ga(instance, fd, qs, info)) {
+ LOG_ERR("Failed to add ga to graph adjacency manager list.");
+ free(info);
+ return -1;
+ }
+
+ return 0;
+}
+
+int gam_flow_wait(struct gam * instance,
+ int * fd,
+ struct cacep_info ** info,
+ qosspec_t * qs)
+{
+ struct ga * ga;
+
+ assert(fd);
+ assert(info);
+ assert(qs);
+
+ pthread_mutex_lock(&instance->gas_lock);
+
+ while (list_empty(&instance->gas))
+ pthread_cond_wait(&instance->gas_cond, &instance->gas_lock);
+
+ ga = list_first_entry((&instance->gas), struct ga, next);
+ if (ga == NULL) {
+ pthread_mutex_unlock(&instance->gas_lock);
+ LOG_ERR("Ga was NULL.");
+ return -1;
+ }
+
+ *fd = ga->fd;
+ *info = ga->info;
+ *qs = ga->qs;
+
+ list_del(&ga->next);
+ free(ga);
+
+ pthread_mutex_unlock(&instance->gas_lock);
+
+ return 0;
+}
diff --git a/src/ipcpd/normal/gam.h b/src/ipcpd/normal/gam.h
new file mode 100644
index 00000000..309cb46d
--- /dev/null
+++ b/src/ipcpd/normal/gam.h
@@ -0,0 +1,44 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2017
+ *
+ * Graph adjacency manager for IPC Process components
+ *
+ * Dimitri Staessens <dimitri.staessens@intec.ugent.be>
+ * Sander Vrijders <sander.vrijders@intec.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.
+ */
+
+#ifndef OUROBOROS_IPCPD_NORMAL_GAM_H
+#define OUROBOROS_IPCPD_NORMAL_GAM_H
+
+/* FIXME: Will take a policy */
+struct gam * gam_create(char * ae_name);
+
+void gam_destroy(struct gam * instance);
+
+int gam_flow_arr(struct gam * instance,
+ int fd,
+ qosspec_t qs);
+
+int gam_flow_alloc(struct gam * instance,
+ char * dst_name,
+ qosspec_t qs);
+
+int gam_flow_wait(struct gam * instance,
+ int * fd,
+ struct cacep_info ** info,
+ qosspec_t * qs);
+
+#endif
diff --git a/src/ipcpd/normal/main.c b/src/ipcpd/normal/main.c
index 8db754aa..85f56ab0 100644
--- a/src/ipcpd/normal/main.c
+++ b/src/ipcpd/normal/main.c
@@ -26,6 +26,7 @@
#include <ouroboros/dev.h>
#include <ouroboros/ipcp-dev.h>
#include <ouroboros/time_utils.h>
+#include <ouroboros/irm.h>
#include "fmgr.h"
#include "ribmgr.h"
@@ -47,7 +48,9 @@ int irmd_api;
pthread_t acceptor;
-void ipcp_sig_handler(int sig, siginfo_t * info, void * c)
+void ipcp_sig_handler(int sig,
+ siginfo_t * info,
+ void * c)
{
(void) c;
@@ -55,7 +58,7 @@ void ipcp_sig_handler(int sig, siginfo_t * info, void * c)
case SIGINT:
case SIGTERM:
case SIGHUP:
- if (info->si_pid == irmd_api) {
+ if (info->si_pid == ipcpi.irmd_api) {
pthread_rwlock_wrlock(&ipcpi.state_lock);
if (ipcp_get_state() == IPCP_INIT)
@@ -101,7 +104,7 @@ static void * flow_acceptor(void * o)
if (strcmp(ae_name, MGMT_AE) == 0) {
ribmgr_add_nm1_flow(fd);
} else if (strcmp(ae_name, DT_AE) == 0) {
- fmgr_nm1_add_flow(fd);
+ fmgr_nm1_flow_arr(fd, qs);
} else {
LOG_DBG("Flow allocation request for unknown AE %s.",
ae_name);
@@ -195,12 +198,6 @@ static int normal_ipcp_enroll(char * dst_name)
pthread_rwlock_unlock(&ipcpi.state_lock);
- /* FIXME: Remove once we obtain neighbors during enrollment */
- if (fmgr_nm1_dt_flow(dst_name, QOS_CUBE_BE)) {
- LOG_ERR("Failed to establish data transfer flow.");
- return -1;
- }
-
LOG_DBG("Enrolled with %s.", dst_name);
return 0;
@@ -296,10 +293,11 @@ static struct ipcp_ops normal_ops = {
.ipcp_flow_dealloc = fmgr_np1_dealloc
};
-int main(int argc, char * argv[])
+int main(int argc,
+ char * argv[])
{
struct sigaction sig_act;
- sigset_t sigset;
+ sigset_t sigset;
if (ap_init(argv[0])) {
LOG_ERR("Failed to init AP");
@@ -317,8 +315,11 @@ int main(int argc, char * argv[])
exit(EXIT_FAILURE);
}
- /* store the process id of the irmd */
- irmd_api = atoi(argv[1]);
+ if (irm_bind_api(getpid(), ipcpi.name)) {
+ LOG_ERR("Failed to bind AP name.");
+ close_logfile();
+ exit(EXIT_FAILURE);
+ }
/* init sig_act */
memset(&sig_act, 0, sizeof(sig_act));
diff --git a/src/ipcpd/normal/ribmgr.c b/src/ipcpd/normal/ribmgr.c
index 6356d48c..c780bf50 100644
--- a/src/ipcpd/normal/ribmgr.c
+++ b/src/ipcpd/normal/ribmgr.c
@@ -1577,8 +1577,8 @@ ssize_t ro_children(const char * name, char *** children)
}
child = node->child;
- **children = malloc(len);
- if (**children == NULL) {
+ *children = malloc(len);
+ if (*children == NULL) {
pthread_mutex_unlock(&rib.ro_lock);
return -1;
}
@@ -1590,7 +1590,7 @@ ssize_t ro_children(const char * name, char *** children)
free((*children)[i]);
i--;
}
- free(**children);
+ free(*children);
pthread_mutex_unlock(&rib.ro_lock);
return -1;
}
diff --git a/src/ipcpd/shim-eth-llc/main.c b/src/ipcpd/shim-eth-llc/main.c
index 0ff8007b..da496b07 100644
--- a/src/ipcpd/shim-eth-llc/main.c
+++ b/src/ipcpd/shim-eth-llc/main.c
@@ -77,9 +77,6 @@ typedef ShimEthLlcMsg shim_eth_llc_msg_t;
#define EVENT_WAIT_TIMEOUT 100 /* us */
#define NAME_QUERY_TIMEOUT 100000000 /* ns */
-/* global for trapping signal */
-int irmd_api;
-
struct eth_llc_frame {
uint8_t dst_hwaddr[MAC_SIZE];
uint8_t src_hwaddr[MAC_SIZE];
@@ -675,7 +672,7 @@ void ipcp_sig_handler(int sig, siginfo_t * info, void * c)
case SIGINT:
case SIGTERM:
case SIGHUP:
- if (info->si_pid == irmd_api) {
+ if (info->si_pid == ipcpi.irmd_api) {
pthread_rwlock_wrlock(&ipcpi.state_lock);
if (ipcp_get_state() == IPCP_INIT)
@@ -1123,9 +1120,6 @@ int main(int argc, char * argv[])
exit(EXIT_FAILURE);
}
- /* store the process id of the irmd */
- irmd_api = atoi(argv[1]);
-
/* init sig_act */
memset(&sig_act, 0, sizeof(sig_act));
diff --git a/src/ipcpd/shim-udp/main.c b/src/ipcpd/shim-udp/main.c
index 8c0c0aac..99aac40e 100644
--- a/src/ipcpd/shim-udp/main.c
+++ b/src/ipcpd/shim-udp/main.c
@@ -60,9 +60,6 @@ typedef ShimUdpMsg shim_udp_msg_t;
#define UDP_MAX_PORTS 0xFFFF
-/* global for trapping signal */
-int irmd_api;
-
struct uf {
int udp;
int skfd;
@@ -529,7 +526,7 @@ void ipcp_sig_handler(int sig, siginfo_t * info, void * c)
case SIGINT:
case SIGTERM:
case SIGHUP:
- if (info->si_pid == irmd_api) {
+ if (info->si_pid == ipcpi.irmd_api) {
pthread_rwlock_wrlock(&ipcpi.state_lock);
if (ipcp_get_state() == IPCP_INIT)
@@ -1191,9 +1188,6 @@ int main(int argc, char * argv[])
exit(EXIT_FAILURE);
}
- /* store the process id of the irmd */
- irmd_api = atoi(argv[1]);
-
/* init sig_act */
memset(&sig_act, 0, sizeof(sig_act));
diff --git a/src/irmd/ipcp.c b/src/irmd/ipcp.c
index cad4dd88..f16587e1 100644
--- a/src/irmd/ipcp.c
+++ b/src/irmd/ipcp.c
@@ -100,7 +100,7 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t api, ipcp_msg_t * msg)
return recv_msg;
}
-pid_t ipcp_create(enum ipcp_type ipcp_type)
+pid_t ipcp_create(char * name, enum ipcp_type ipcp_type)
{
pid_t api = -1;
char irmd_api[10];
@@ -109,7 +109,7 @@ pid_t ipcp_create(enum ipcp_type ipcp_type)
char * full_name = NULL;
char * exec_name = NULL;
char * log_file = NULL;
- char * argv[4];
+ char * argv[5];
sprintf(irmd_api, "%u", getpid());
@@ -119,9 +119,8 @@ pid_t ipcp_create(enum ipcp_type ipcp_type)
return api;
}
- if (api != 0) {
+ if (api != 0)
return api;
- }
if (ipcp_type == IPCP_NORMAL)
exec_name = IPCP_NORMAL_EXEC;
@@ -162,8 +161,9 @@ pid_t ipcp_create(enum ipcp_type ipcp_type)
/* log_file to be placed at the end */
argv[0] = full_name;
argv[1] = irmd_api;
- argv[2] = log_file;
- argv[3] = NULL;
+ argv[2] = name;
+ argv[3] = log_file;
+ argv[4] = NULL;
execv(argv[0], &argv[0]);
diff --git a/src/irmd/ipcp.h b/src/irmd/ipcp.h
index 429e0d5d..658aa2ea 100644
--- a/src/irmd/ipcp.h
+++ b/src/irmd/ipcp.h
@@ -28,8 +28,8 @@
#ifndef OUROBOROS_IPCP_H
#define OUROBOROS_IPCP_H
-/* Returns the process id */
-pid_t ipcp_create(enum ipcp_type ipcp_type);
+pid_t ipcp_create(char * name,
+ enum ipcp_type ipcp_type);
int ipcp_destroy(pid_t api);
@@ -45,7 +45,7 @@ int ipcp_name_reg(pid_t api,
int ipcp_name_unreg(pid_t api,
char * name);
-int ipcp_name_query(pid_t api,
+int ipcp_name_query(pid_t api,
char * name);
int ipcp_flow_alloc(pid_t api,
diff --git a/src/irmd/main.c b/src/irmd/main.c
index 9dc08cbe..435ee116 100644
--- a/src/irmd/main.c
+++ b/src/irmd/main.c
@@ -245,7 +245,7 @@ static pid_t create_ipcp(char * name, enum ipcp_type ipcp_type)
pthread_rwlock_wrlock(&irmd->reg_lock);
- api->pid = ipcp_create(ipcp_type);
+ api->pid = ipcp_create(name, ipcp_type);
if (api->pid == -1) {
pthread_rwlock_unlock(&irmd->reg_lock);
pthread_rwlock_unlock(&irmd->state_lock);
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index 22971806..f5273904 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -8,8 +8,8 @@ protobuf_generate_c(IRM_PROTO_SRCS IRM_PROTO_HDRS irmd_messages.proto)
protobuf_generate_c(IPCP_PROTO_SRCS IPCP_PROTO_HDRS ipcpd_messages.proto)
protobuf_generate_c(DIF_CONFIG_PROTO_SRCS DIF_CONFIG_PROTO_HDRS
dif_config.proto)
-protobuf_generate_c(CDAP_PROTO_SRCS CDAP_PROTO_HDRS
- cdap.proto)
+protobuf_generate_c(CDAP_PROTO_SRCS CDAP_PROTO_HDRS cdap.proto)
+protobuf_generate_c(CACEP_PROTO_SRCS CACEP_PROTO_HDRS cacep.proto)
if(NOT APPLE)
find_library(LIBRT_LIBRARIES rt)
@@ -28,6 +28,7 @@ endif()
set(SOURCE_FILES
# Add source files here
bitmap.c
+ cacep.c
cdap.c
cdap_req.c
dev.c
@@ -45,9 +46,9 @@ set(SOURCE_FILES
utils.c
)
-add_library(ouroboros SHARED ${SOURCE_FILES}
- ${IRM_PROTO_SRCS} ${IPCP_PROTO_SRCS}
- ${DIF_CONFIG_PROTO_SRCS} ${CDAP_PROTO_SRCS})
+add_library(ouroboros SHARED ${SOURCE_FILES} ${IRM_PROTO_SRCS}
+ ${IPCP_PROTO_SRCS} ${DIF_CONFIG_PROTO_SRCS}
+ ${CDAP_PROTO_SRCS} ${CACEP_PROTO_SRCS})
target_link_libraries(ouroboros ${LIBRT_LIBRARIES}
${LIBPTHREAD_LIBRARIES} ${PROTOBUF_C_LIBRARY})
diff --git a/src/lib/cacep.c b/src/lib/cacep.c
new file mode 100644
index 00000000..90994c04
--- /dev/null
+++ b/src/lib/cacep.c
@@ -0,0 +1,170 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2017
+ *
+ * The Common Application Connection Establishment Phase
+ *
+ * Sander Vrijders <sander.vrijders@intec.ugent.be>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <ouroboros/config.h>
+#include <ouroboros/cacep.h>
+#include <ouroboros/dev.h>
+#include <ouroboros/errno.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "cacep.pb-c.h"
+typedef Cacep cacep_t;
+
+#define BUF_SIZE 2048
+
+struct cacep {
+ int fd;
+ char * name;
+ uint64_t address;
+};
+
+struct cacep * cacep_create(int fd,
+ char * name,
+ uint64_t address)
+{
+ struct cacep * tmp;
+
+ tmp = malloc(sizeof(*tmp));
+ if (tmp == NULL)
+ return NULL;
+
+ tmp->fd = fd;
+ tmp->address = address;
+ tmp->name = strdup(name);
+ if (tmp->name == NULL) {
+ free(tmp);
+ return NULL;
+ }
+
+ return tmp;
+}
+
+int cacep_destroy(struct cacep * instance)
+{
+ if (instance == NULL)
+ return 0;
+
+ free(instance);
+
+ return 0;
+}
+
+static struct cacep_info * read_msg(struct cacep * instance)
+{
+ struct cacep_info * tmp;
+ uint8_t buf[BUF_SIZE];
+ cacep_t * msg;
+ ssize_t len;
+
+ len = flow_read(instance->fd, buf, BUF_SIZE);
+ if (len < 0)
+ return NULL;
+
+ msg = cacep__unpack(NULL, len, buf);
+ if (msg == NULL)
+ return NULL;
+
+ tmp = malloc(sizeof(*tmp));
+ if (tmp == NULL) {
+ cacep__free_unpacked(msg, NULL);
+ return NULL;
+ }
+
+ tmp->addr = msg->address;
+ tmp->name = strdup(msg->name);
+ if (tmp->name == NULL) {
+ free(tmp);
+ cacep__free_unpacked(msg, NULL);
+ return NULL;
+ }
+
+ cacep__free_unpacked(msg, NULL);
+
+ return tmp;
+}
+
+static int send_msg(struct cacep * instance)
+{
+ cacep_t msg = CACEP__INIT;
+ int ret = 0;
+ uint8_t * data = NULL;
+ size_t len = 0;
+
+ msg.name = instance->name;
+ msg.address = instance->address;
+
+ len = cacep__get_packed_size(&msg);
+ if (len == 0)
+ return -1;
+
+ data = malloc(len);
+ if (data == NULL)
+ return -ENOMEM;
+
+ cacep__pack(&msg, data);
+
+ if (flow_write(instance->fd, data, len) < 0)
+ ret = -1;
+
+ free(data);
+
+ return ret;
+}
+
+struct cacep_info * cacep_auth(struct cacep * instance)
+{
+ struct cacep_info * tmp;
+
+ if (instance == NULL)
+ return NULL;
+
+ if (send_msg(instance))
+ return NULL;
+
+ tmp = read_msg(instance);
+ if (tmp == NULL)
+ return NULL;
+
+ return tmp;
+}
+
+struct cacep_info * cacep_auth_wait(struct cacep * instance)
+{
+ struct cacep_info * tmp;
+
+ if (instance == NULL)
+ return NULL;
+
+ tmp = read_msg(instance);
+ if (tmp == NULL)
+ return NULL;
+
+ if (send_msg(instance)) {
+ free(tmp->name);
+ free(tmp);
+ return NULL;
+ }
+
+ return tmp;
+}
diff --git a/src/lib/cacep.proto b/src/lib/cacep.proto
new file mode 100644
index 00000000..603b095d
--- /dev/null
+++ b/src/lib/cacep.proto
@@ -0,0 +1,29 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2017
+ *
+ * CACEP message
+ *
+ * Dimitri Staessens <dimitri.staessens@intec.ugent.be>
+ * Sander Vrijders <sander.vrijders@intec.ugent.be>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+syntax = "proto2";
+
+message cacep {
+ required string name = 1;
+ required uint64 address = 2;
+}
diff --git a/src/tools/irm/irm_ipcp_bootstrap.c b/src/tools/irm/irm_ipcp_bootstrap.c
index 8fd2fb73..10e53294 100644
--- a/src/tools/irm/irm_ipcp_bootstrap.c
+++ b/src/tools/irm/irm_ipcp_bootstrap.c
@@ -199,8 +199,6 @@ int do_bootstrap_ipcp(int argc, char ** argv)
api = irm_create_ipcp(name, conf.type);
if (api == 0)
return -1;
- if (conf.type == IPCP_NORMAL)
- irm_bind_api(api, name);
len = irm_list_ipcps(name, &apis);
}
diff --git a/src/tools/irm/irm_ipcp_create.c b/src/tools/irm/irm_ipcp_create.c
index 9f636f34..e8ed1186 100644
--- a/src/tools/irm/irm_ipcp_create.c
+++ b/src/tools/irm/irm_ipcp_create.c
@@ -85,8 +85,5 @@ int do_create_ipcp(int argc, char ** argv)
if (api == 0)
return -1;
- if (type == IPCP_NORMAL)
- irm_bind_api(api, ipcp_name);
-
return 0;
}
diff --git a/src/tools/irm/irm_ipcp_enroll.c b/src/tools/irm/irm_ipcp_enroll.c
index f7807f42..3731fa81 100644
--- a/src/tools/irm/irm_ipcp_enroll.c
+++ b/src/tools/irm/irm_ipcp_enroll.c
@@ -68,7 +68,6 @@ int do_enroll_ipcp(int argc, char ** argv)
api = irm_create_ipcp(name, IPCP_NORMAL);
if (api == 0)
return -1;
- irm_bind_api(api, name);
len = irm_list_ipcps(name, &apis);
}