summaryrefslogtreecommitdiff
path: root/src/ipcpd/unicast/pol/graph.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipcpd/unicast/pol/graph.c')
-rw-r--r--src/ipcpd/unicast/pol/graph.c849
1 files changed, 0 insertions, 849 deletions
diff --git a/src/ipcpd/unicast/pol/graph.c b/src/ipcpd/unicast/pol/graph.c
deleted file mode 100644
index 6ea5c507..00000000
--- a/src/ipcpd/unicast/pol/graph.c
+++ /dev/null
@@ -1,849 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2021
- *
- * Undirected graph structure
- *
- * Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
- * Nick Aerts <nick.aerts@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., http://www.fsf.org/about/contact/.
- */
-
-#if defined(__linux__) || defined(__CYGWIN__)
-#define _DEFAULT_SOURCE
-#else
-#define _POSIX_C_SOURCE 200112L
-#endif
-
-#define OUROBOROS_PREFIX "graph"
-
-#include <ouroboros/logs.h>
-#include <ouroboros/errno.h>
-#include <ouroboros/list.h>
-
-#include "graph.h"
-#include "ipcp.h"
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-
-struct vertex {
- struct list_head next;
- uint64_t addr;
- struct list_head edges;
- int index;
-};
-
-struct edge {
- struct list_head next;
- struct vertex * nb;
- qosspec_t qs;
- int announced;
-};
-
-struct graph {
- size_t nr_vertices;
- struct list_head vertices;
- pthread_mutex_t lock;
-};
-
-static struct edge * find_edge_by_addr(struct vertex * vertex,
- uint64_t dst_addr)
-{
- struct list_head * p;
-
- assert(vertex);
-
- list_for_each(p, &vertex->edges) {
- struct edge * e = list_entry(p, struct edge, next);
- if (e->nb->addr == dst_addr)
- return e;
- }
-
- return NULL;
-}
-
-static struct vertex * find_vertex_by_addr(struct graph * graph,
- uint64_t addr)
-{
- struct list_head * p;
-
- assert(graph);
-
- list_for_each(p, &graph->vertices) {
- struct vertex * e = list_entry(p, struct vertex, next);
- if (e->addr == addr)
- return e;
- }
-
- return NULL;
-}
-
-static struct edge * add_edge(struct vertex * vertex,
- struct vertex * nb)
-{
- struct edge * edge;
-
- assert(vertex);
- assert(nb);
-
- edge = malloc(sizeof(*edge));
- if (edge == NULL)
- return NULL;
-
- edge->nb = nb;
- edge->announced = 0;
-
- list_add(&edge->next, &vertex->edges);
-
- return edge;
-}
-
-static void del_edge(struct edge * edge)
-{
- assert(edge);
-
- list_del(&edge->next);
- free(edge);
-}
-
-static struct vertex * add_vertex(struct graph * graph,
- uint64_t addr)
-{
- struct vertex * vertex;
- struct list_head * p;
- int i = 0;
-
- assert(graph);
-
- vertex = malloc(sizeof(*vertex));
- if (vertex == NULL)
- return NULL;
-
- list_head_init(&vertex->edges);
- vertex->addr = addr;
-
- /* Keep them ordered on address. */
- list_for_each(p, &graph->vertices) {
- struct vertex * v = list_entry(p, struct vertex, next);
- if (v->addr > addr)
- break;
- i++;
- }
-
- vertex->index = i;
-
- list_add_tail(&vertex->next, p);
-
- /* Increase the index of the vertices to the right. */
- list_for_each(p, &graph->vertices) {
- struct vertex * v = list_entry(p, struct vertex, next);
- if (v->addr > addr)
- v->index++;
- }
-
- graph->nr_vertices++;
-
- return vertex;
-}
-
-static void del_vertex(struct graph * graph,
- struct vertex * vertex)
-{
- struct list_head * p;
- struct list_head * h;
-
- assert(graph);
- assert(vertex);
-
- list_del(&vertex->next);
-
- /* Decrease the index of the vertices to the right. */
- list_for_each(p, &graph->vertices) {
- struct vertex * v = list_entry(p, struct vertex, next);
- if (v->addr > vertex->addr)
- v->index--;
- }
-
- list_for_each_safe(p, h, &vertex->edges) {
- struct edge * e = list_entry(p, struct edge, next);
- del_edge(e);
- }
-
- free(vertex);
-
- graph->nr_vertices--;
-}
-
-struct graph * graph_create(void)
-{
- struct graph * graph;
-
- graph = malloc(sizeof(*graph));
- if (graph == NULL)
- return NULL;
-
- if (pthread_mutex_init(&graph->lock, NULL)) {
- free(graph);
- return NULL;
- }
-
- graph->nr_vertices = 0;
- list_head_init(&graph->vertices);
-
- return graph;
-}
-
-void graph_destroy(struct graph * graph)
-{
- struct list_head * p = NULL;
- struct list_head * n = NULL;
-
- assert(graph);
-
- pthread_mutex_lock(&graph->lock);
-
- list_for_each_safe(p, n, &graph->vertices) {
- struct vertex * e = list_entry(p, struct vertex, next);
- del_vertex(graph, e);
- }
-
- pthread_mutex_unlock(&graph->lock);
-
- pthread_mutex_destroy(&graph->lock);
-
- free(graph);
-}
-
-int graph_update_edge(struct graph * graph,
- uint64_t s_addr,
- uint64_t d_addr,
- qosspec_t qs)
-{
- struct vertex * v;
- struct edge * e;
- struct vertex * nb;
- struct edge * nb_e;
-
- assert(graph);
-
- pthread_mutex_lock(&graph->lock);
-
- v = find_vertex_by_addr(graph, s_addr);
- if (v == NULL) {
- v = add_vertex(graph, s_addr);
- if (v == NULL) {
- pthread_mutex_unlock(&graph->lock);
- log_err("Failed to add vertex.");
- return -ENOMEM;
- }
- }
-
- nb = find_vertex_by_addr(graph, d_addr);
- if (nb == NULL) {
- nb = add_vertex(graph, d_addr);
- if (nb == NULL) {
- if (list_is_empty(&v->edges))
- del_vertex(graph, v);
- pthread_mutex_unlock(&graph->lock);
- log_err("Failed to add vertex.");
- return -ENOMEM;
- }
- }
-
- e = find_edge_by_addr(v, d_addr);
- if (e == NULL) {
- e = add_edge(v, nb);
- if (e == NULL) {
- if (list_is_empty(&v->edges))
- del_vertex(graph, v);
- if (list_is_empty(&nb->edges))
- del_vertex(graph, nb);
- pthread_mutex_unlock(&graph->lock);
- log_err("Failed to add edge.");
- return -ENOMEM;
- }
- }
-
- e->announced++;
- e->qs = qs;
-
- nb_e = find_edge_by_addr(nb, s_addr);
- if (nb_e == NULL) {
- nb_e = add_edge(nb, v);
- if (nb_e == NULL) {
- if (--e->announced == 0)
- del_edge(e);
- if (list_is_empty(&v->edges))
- del_vertex(graph, v);
- if (list_is_empty(&nb->edges))
- del_vertex(graph, nb);
- pthread_mutex_unlock(&graph->lock);
- log_err("Failed to add edge.");
- return -ENOMEM;
- }
- }
-
- nb_e->announced++;
- nb_e->qs = qs;
-
- pthread_mutex_unlock(&graph->lock);
-
- return 0;
-}
-
-int graph_del_edge(struct graph * graph,
- uint64_t s_addr,
- uint64_t d_addr)
-{
- struct vertex * v;
- struct edge * e;
- struct vertex * nb;
- struct edge * nb_e;
-
- assert(graph);
-
- pthread_mutex_lock(&graph->lock);
-
- v = find_vertex_by_addr(graph, s_addr);
- if (v == NULL) {
- pthread_mutex_unlock(&graph->lock);
- log_err("No such source vertex.");
- return -1;
- }
-
- nb = find_vertex_by_addr(graph, d_addr);
- if (nb == NULL) {
- pthread_mutex_unlock(&graph->lock);
- log_err("No such destination vertex.");
- return -1;
- }
-
- e = find_edge_by_addr(v, d_addr);
- if (e == NULL) {
- pthread_mutex_unlock(&graph->lock);
- log_err("No such source edge.");
- return -1;
- }
-
- nb_e = find_edge_by_addr(nb, s_addr);
- if (nb_e == NULL) {
- pthread_mutex_unlock(&graph->lock);
- log_err("No such destination edge.");
- return -1;
- }
-
- if (--e->announced == 0)
- del_edge(e);
- if (--nb_e->announced == 0)
- del_edge(nb_e);
-
- /* Removing vertex if it was the last edge */
- if (list_is_empty(&v->edges))
- del_vertex(graph, v);
- if (list_is_empty(&nb->edges))
- del_vertex(graph, nb);
-
- pthread_mutex_unlock(&graph->lock);
-
- return 0;
-}
-
-static int get_min_vertex(struct graph * graph,
- int * dist,
- bool * used,
- struct vertex ** v)
-{
- int min = INT_MAX;
- int index = -1;
- int i = 0;
- struct list_head * p;
-
- assert(v);
- assert(graph);
- assert(dist);
- assert(used);
-
- *v = NULL;
-
- list_for_each(p, &graph->vertices) {
- if (!used[i] && dist[i] < min) {
- min = dist[i];
- index = i;
- *v = list_entry(p, struct vertex, next);
- }
-
- i++;
- }
-
- if (index != -1)
- used[index] = true;
-
- return index;
-}
-
-static int dijkstra(struct graph * graph,
- uint64_t src,
- struct vertex *** nhops,
- int ** dist)
-{
- bool * used;
- struct list_head * p = NULL;
- int i = 0;
- struct vertex * v = NULL;
- struct edge * e = NULL;
- int alt;
-
- assert(graph);
- assert(nhops);
- assert(dist);
-
- *nhops = malloc(sizeof(**nhops) * graph->nr_vertices);
- if (*nhops == NULL)
- goto fail_pnhops;
-
- *dist = malloc(sizeof(**dist) * graph->nr_vertices);
- if (*dist == NULL)
- goto fail_pdist;
-
- used = malloc(sizeof(*used) * graph->nr_vertices);
- if (used == NULL)
- goto fail_used;
-
- /* Init the data structures */
- memset(used, 0, sizeof(*used) * graph->nr_vertices);
- memset(*nhops, 0, sizeof(**nhops) * graph->nr_vertices);
- memset(*dist, 0, sizeof(**dist) * graph->nr_vertices);
-
- list_for_each(p, &graph->vertices) {
- v = list_entry(p, struct vertex, next);
- (*dist)[i++] = (v->addr == src) ? 0 : INT_MAX;
- }
-
- /* Perform actual Dijkstra */
- i = get_min_vertex(graph, *dist, used, &v);
- while (v != NULL) {
- list_for_each(p, &v->edges) {
- e = list_entry(p, struct edge, next);
-
- /* Only include it if both sides announced it. */
- if (e->announced != 2)
- continue;
-
- /*
- * NOTE: Current weight is just hop count.
- * Method could be extended to use a different
- * weight for a different QoS cube.
- */
- alt = (*dist)[i] + 1;
- if (alt < (*dist)[e->nb->index]) {
- (*dist)[e->nb->index] = alt;
- if (v->addr == src)
- (*nhops)[e->nb->index] = e->nb;
- else
- (*nhops)[e->nb->index] = (*nhops)[i];
- }
- }
- i = get_min_vertex(graph, *dist, used, &v);
- }
-
- free(used);
-
- return 0;
-
- fail_used:
- free(*dist);
- fail_pdist:
- free(*nhops);
- fail_pnhops:
- return -1;
-
-}
-
-static void free_routing_table(struct list_head * table)
-{
- struct list_head * h;
- struct list_head * p;
- struct list_head * q;
- struct list_head * i;
-
- assert(table);
-
- list_for_each_safe(p, h, table) {
- struct routing_table * t =
- list_entry(p, struct routing_table, next);
- list_for_each_safe(q, i, &t->nhops) {
- struct nhop * n =
- list_entry(q, struct nhop, next);
- list_del(&n->next);
- free(n);
- }
- list_del(&t->next);
- free(t);
- }
-}
-
-void graph_free_routing_table(struct graph * graph,
- struct list_head * table)
-{
- assert(table);
-
- pthread_mutex_lock(&graph->lock);
-
- free_routing_table(table);
-
- pthread_mutex_unlock(&graph->lock);
-}
-
-static int graph_routing_table_simple(struct graph * graph,
- uint64_t s_addr,
- struct list_head * table,
- int ** dist)
-{
- struct vertex ** nhops;
- struct list_head * p;
- int i = 0;
- struct vertex * v;
- struct routing_table * t;
- struct nhop * n;
-
- assert(graph);
- assert(table);
- assert(dist);
-
- /* We need at least 2 vertices for a table */
- if (graph->nr_vertices < 2)
- goto fail_vertices;
-
- if (dijkstra(graph, s_addr, &nhops, dist))
- goto fail_vertices;
-
- list_head_init(table);
-
- /* Now construct the routing table from the nhops. */
- list_for_each(p, &graph->vertices) {
- v = list_entry(p, struct vertex, next);
-
- /* This is the src */
- if (nhops[i] == NULL) {
- i++;
- continue;
- }
-
- t = malloc(sizeof(*t));
- if (t == NULL)
- goto fail_t;
-
- list_head_init(&t->nhops);
-
- n = malloc(sizeof(*n));
- if (n == NULL)
- goto fail_n;
-
- t->dst = v->addr;
- n->nhop = nhops[i]->addr;
-
- list_add(&n->next, &t->nhops);
- list_add(&t->next, table);
-
- i++;
- }
-
- free(nhops);
-
- return 0;
-
- fail_n:
- free(t);
- fail_t:
- free_routing_table(table);
- free(nhops);
- free(*dist);
- fail_vertices:
- *dist = NULL;
- return -1;
-}
-
-static int add_lfa_to_table(struct list_head * table,
- uint64_t addr,
- uint64_t lfa)
-{
- struct list_head * p;
- struct nhop * n;
-
- assert(table);
-
- n = malloc(sizeof(*n));
- if (n == NULL)
- return -1;
-
- n->nhop = lfa;
-
- list_for_each(p, table) {
- struct routing_table * t =
- list_entry(p, struct routing_table, next);
- if (t->dst == addr) {
- list_add_tail(&n->next, &t->nhops);
- return 0;
- }
- }
-
- free(n);
-
- return -1;
-}
-
-static int graph_routing_table_lfa(struct graph * graph,
- uint64_t s_addr,
- struct list_head * table,
- int ** dist)
-{
- int * n_dist[PROG_MAX_FLOWS];
- uint64_t addrs[PROG_MAX_FLOWS];
- int n_index[PROG_MAX_FLOWS];
- struct list_head * p;
- struct list_head * q;
- struct vertex * v;
- struct edge * e;
- struct vertex ** nhops;
- int i = 0;
- int j;
- int k;
-
- if (graph_routing_table_simple(graph, s_addr, table, dist))
- goto fail_table;
-
- for (j = 0; j < PROG_MAX_FLOWS; j++) {
- n_dist[j] = NULL;
- n_index[j] = -1;
- addrs[j] = -1;
- }
-
- list_for_each(p, &graph->vertices) {
- v = list_entry(p, struct vertex, next);
-
- if (v->addr != s_addr)
- continue;
-
- /*
- * Get the distances for every neighbor
- * of the source.
- */
- list_for_each(q, &v->edges) {
- e = list_entry(q, struct edge, next);
-
- addrs[i] = e->nb->addr;
- n_index[i] = e->nb->index;
- if (dijkstra(graph, e->nb->addr,
- &nhops, &(n_dist[i++])))
- goto fail_dijkstra;
-
- free(nhops);
- }
-
- break;
- }
-
- /* Loop though all nodes to see if we have a LFA for them. */
- list_for_each(p, &graph->vertices) {
- v = list_entry(p, struct vertex, next);
-
- if (v->addr == s_addr)
- continue;
-
- /*
- * Check for every neighbor if
- * dist(neighbor, destination) <
- * dist(neighbor, source) + dist(source, destination).
- */
- for (j = 0; j < i; j++) {
- /* Exclude ourselves. */
- if (addrs[j] == v->addr)
- continue;
-
- if (n_dist[j][v->index] <
- (*dist)[n_index[j]] + (*dist)[v->index])
- if (add_lfa_to_table(table, v->addr,
- addrs[j]))
- goto fail_add_lfa;
- }
- }
-
- for (j = 0; j < i; j++)
- free(n_dist[j]);
-
- return 0;
-
- fail_add_lfa:
- for (k = j; k < i; k++)
- free(n_dist[k]);
- fail_dijkstra:
- free_routing_table(table);
- fail_table:
- return -1;
-}
-
-static int graph_routing_table_ecmp(struct graph * graph,
- uint64_t s_addr,
- struct list_head * table,
- int ** dist)
-{
- struct vertex ** nhops;
- struct list_head * p;
- struct list_head * h;
- size_t i;
- struct vertex * v;
- struct vertex * src_v;
- struct edge * e;
- struct routing_table * t;
- struct nhop * n;
- struct list_head * forwarding;
-
- assert(graph);
- assert(dist);
-
- if (graph-> nr_vertices < 2)
- goto fail_vertices;
-
- forwarding = malloc(sizeof(*forwarding) * graph->nr_vertices);
- if (forwarding == NULL)
- goto fail_vertices;
-
- for (i = 0; i < graph->nr_vertices; ++i)
- list_head_init(&forwarding[i]);
-
- if (dijkstra(graph, s_addr, &nhops, dist))
- goto fail_dijkstra;
-
- free(nhops);
-
- src_v = find_vertex_by_addr(graph, s_addr);
- if (src_v == NULL)
- goto fail_src_v;
-
- list_for_each(p, &src_v->edges) {
- int * tmp_dist;
-
- e = list_entry(p, struct edge, next);
- if (dijkstra(graph, e->nb->addr, &nhops, &tmp_dist))
- goto fail_src_v;
-
- free(nhops);
-
- list_for_each(h, &graph->vertices) {
- v = list_entry(h, struct vertex, next);
- if (tmp_dist[v->index] + 1 == (*dist)[v->index]) {
- n = malloc(sizeof(*n));
- if (n == NULL) {
- free(tmp_dist);
- goto fail_src_v;
- }
- n->nhop = e->nb->addr;
- list_add_tail(&n->next, &forwarding[v->index]);
- }
- }
-
- free(tmp_dist);
- }
-
- list_head_init(table);
- i = 0;
- list_for_each(p, &graph->vertices) {
- v = list_entry(p, struct vertex, next);
- if (v->addr == s_addr) {
- ++i;
- continue;
- }
-
- t = malloc(sizeof(*t));
- if (t == NULL)
- goto fail_t;
-
- t->dst = v->addr;
-
- list_head_init(&t->nhops);
- if (&forwarding[i] != forwarding[i].nxt) {
- t->nhops.nxt = forwarding[i].nxt;
- t->nhops.prv = forwarding[i].prv;
- forwarding[i].prv->nxt = &t->nhops;
- forwarding[i].nxt->prv = &t->nhops;
- }
-
- list_add(&t->next, table);
- ++i;
- }
-
- free(*dist);
- *dist = NULL;
- free(forwarding);
-
- return 0;
-
- fail_t:
- free_routing_table(table);
- fail_src_v:
- free(*dist);
- fail_dijkstra:
- free(forwarding);
- fail_vertices:
- *dist = NULL;
- return -1;
-}
-
-int graph_routing_table(struct graph * graph,
- enum routing_algo algo,
- uint64_t s_addr,
- struct list_head * table)
-{
- int * s_dist;
-
- assert(graph);
- assert(table);
-
- pthread_mutex_lock(&graph->lock);
-
- switch (algo) {
- case ROUTING_SIMPLE:
- /* LFA uses the s_dist this returns. */
- if (graph_routing_table_simple(graph, s_addr, table, &s_dist))
- goto fail_table;
- break;
- case ROUTING_LFA:
- if (graph_routing_table_lfa(graph, s_addr, table, &s_dist))
- goto fail_table;
- break;
-
- case ROUTING_ECMP:
- if (graph_routing_table_ecmp(graph, s_addr, table, &s_dist))
- goto fail_table;
- break;
- default:
- log_err("Unsupported algorithm.");
- goto fail_table;
- }
-
- pthread_mutex_unlock(&graph->lock);
-
- free(s_dist);
-
- return 0;
-
- fail_table:
- pthread_mutex_unlock(&graph->lock);
- return -1;
-}