lib: Add CSPF Path Computation algorithm

As helper function of Segment Routing Flex Algo or RSVP-TE
add Constrained Shortest Path First algorithm able to compute
path with constraints. Supported constraints are as follow:
 - Standard IGP metric
 - TE IGP metric
 - Delay metric
 - Bandwidth for given Class of Service for bandwidth reservation (RSVP-TE)

Usage of CSPF algorithms is detailed in the doc/developer/cspf.rst file

Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
This commit is contained in:
Olivier Dugeon 2022-01-13 10:15:51 +01:00
parent 29abd4e319
commit fd8e262ab7
4 changed files with 1055 additions and 0 deletions

196
doc/developer/cspf.rst Normal file
View File

@ -0,0 +1,196 @@
Path Computation Algorithms
===========================
Introduction
------------
Both RSVP-TE and Segment Routing Flex Algo need to compute end to end path
with other constraints as the standard IGP metric. Based on Shortest Path First
(SPF) algorithms, a new class of Constrained SPF (CSPF) is provided by the FRR
library.
Supported constraints are as follow:
- Standard IGP metric (here, CSPF provides the same result as a normal SPF)
- Traffic Engineering (TE) IGP metric
- Delay from the IGP Extended Metrics
- Bandwidth for a given Class of Service (CoS) for bandwidth reservation
Algorithm
---------
The CSPF algorithm is based on a Priority Queue which store the on-going
possible path sorted by their respective weights. This weight corresponds
to the cost of the cuurent path from the source up to the current node.
The algorithm is as followed:
```
cost = MAX_COST;
Priority_Queue.empty();
Visited_Node.empty();
Processed_Path.empty();
src = new_path(source_address);
src.cost = 0;
dst = new_destinatio(destination_address);
dst.cost = MAX_COST;
Processed_Path.add(src);
Processed_Path.add(dst);
while (Priority_Queue.count != 0) {
current_path = Priority_Queue.pop();
current_node = next_path.destination;
Visited_Node.add(current_node);
for (current_node.edges: edge) {
if (prune_edge(current_path, edge)
continue;
if (relax(current_path) && cost > current_path.cost) {
optim_path = current_path;
cost = current_path.cost;
}
}
}
prune_edge(path, edge) {
// check that path + edge meet constraints e.g.
if (current_path.cost + edge.cost > constrained_cost)
return false;
else
return true;
}
relax_edge(current_path, edge) {
next_node = edge.destination;
if (Visited_Node.get(next_node))
return false;
next_path = Processed_Path.get(edge.destination);
if (!next_path) {
next_path = new path(edge.destination);
Processed_Path.add(next_path);
}
total_cost = current_path.cost + edge.cost;
if (total_cost < next_path.cost) {
next_path = current_path;
next_path.add_edge(edge);
next_path.cost = total_cost;
Priority_Queue.add(next_path);
}
return (next_path.destination == destination);
}
```
Definition
----------
.. c:struct:: constraints
This is the constraints structure that contains:
- cost: the total cost that the path must respect
- ctype: type of constraints:
- CSPF_METRIC for standard metric
- CSPF_TE_METRIC for TE metric
- CSPF_DELAY for delay metric
- bw: bandwidth that the path must respect
- cos: Class of Service (COS) for the bandwidth
- family: AF_INET or AF_INET6
- type: RSVP_TE, SR_TE or SRV6_TE
.. c:struct:: c_path
This is the Constraint Path structure that contains:
- edges: List of Edges that compose the path
- status: FAILED, IN_PROGRESS, SUCCESS, NO_SOURCE, NO_DESTINATION, SAME_SRC_DST
- weight: the cost from source to the destination of the path
- dst: key of the destination vertex
.. c:struct:: cspf
This is the main structure for path computation. Even if it is public, you
don't need to set manually the internal field of the structure. Instead, use
the following functions:
.. c:function:: struct cspf *cspf_new(void);
Function to create an empty cspf for future call of path computation
.. c:function:: struct cspf *cspf_init(struct cspf *algo, const struct ls_vertex *src, const struct ls_vertex *dst, struct constraints *csts);
This function initialize the cspf with source and destination vertex and
constraints and return pointer to the cspf structure. If input cspf structure
is NULL, a new cspf structure is allocated and initialize.
.. c:function:: struct cspf *cspf_init_v4(struct cspf *algo, struct ls_ted *ted, const struct in_addr src, const struct in_addr dst, struct constraints *csts);
Same as cspf_init, but here, source and destination vertex are extract from
the TED data base based on respective IPv4 source and destination addresses.
.. c:function:: struct cspf *cspf_init_v6(struct cspf *algo, struct ls_ted *ted, const struct in6_addr src, const struct in6_addr dst, struct constraints *csts);
Same as cspf_init_v4 but with IPv6 source and destination addresses.
.. c:function:: void cspf_clean(struct cspf *algo);
Clean internal structure of cspf in order to reuse it for another path
computation.
.. c:function:: void cspf_del(struct cspf *algo);
Delete cspf structure. A call to cspf_clean() function is perform prior to
free allocated memeory.
.. c:function:: struct c_path *compute_p2p_path(struct ls_ted *ted, struct cspf *algo);
Compute point to point path from the ted and cspf.
The function always return a constraints path. The status of the path gives
indication about the success or failure of the algorithm. If cspf structure has
not been initialize with a call to `cspf_init() or cspf_init_XX()`, the
algorithm returns a constraints path with status set to FAILED.
Note that a call to `cspf_clean()` is performed at the end of this function,
thus it is mandatory to initialize the cspf structure again prior to call again
the path computation algorithm.
Usage
-----
Of course, CSPF algorithm needs a network topology that contains the
various metrics. Link State provides such Traffic Engineering Database.
To perform a Path Computation with given constraints, proceed as follow:
.. code-block:: c
struct cspf *algo;
struct ls_ted *ted;
struct in_addr src;
struct in_addr dst;
struct constraints csts;
struct c_path *path;
// Create a new CSPF structure
algo = cspf_new();
// Initialize constraints
csts.cost = 100;
csts.ctype = CSPF_TE_METRIC;
csts.family = AF_INET;
csts.type = SR_TE;
csts.bw = 1000000;
csts.cos = 3;
// Then, initialise th CSPF with source, destination and constraints
cspf_init_v4(algo, ted, src, dst, &csts);
// Finally, got the Computed Path;
path = compute_p2p_path(ted, algo);
if (path.status == SUCCESS)
zlog_info("Got a valid constraints path");
else
zlog_info("Unable to compute constraints path. Got %d status", path->status);
If you would compute another path, you must call `cspf_init()` prior to
`compute_p2p_path()` to change source, destination and/or constraints.

646
lib/cspf.c Normal file
View File

@ -0,0 +1,646 @@
/*
* Constraints Shortest Path First algorithms - cspf.c
*
* Author: Olivier Dugeon <olivier.dugeon@orange.com>
*
* Copyright (C) 2022 Orange http://www.orange.com
*
* This file is part of Free Range Routing (FRR).
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include "if.h"
#include "linklist.h"
#include "log.h"
#include "hash.h"
#include "memory.h"
#include "prefix.h"
#include "table.h"
#include "stream.h"
#include "printfrr.h"
#include "link_state.h"
#include "cspf.h"
/* Link State Memory allocation */
DEFINE_MTYPE_STATIC(LIB, PCA, "Path Computation Algorithms");
/**
* Create new Constrained Path. Memory is dynamically allocated.
*
* @param key Vertex key of the destination of this path
*
* @return Pointer to a new Constrained Path structure
*/
static struct c_path *cpath_new(uint64_t key)
{
struct c_path *path;
/* Sanity Check */
if (key == 0)
return NULL;
path = XCALLOC(MTYPE_PCA, sizeof(struct c_path));
path->dst = key;
path->status = IN_PROGRESS;
path->edges = list_new();
path->weight = MAX_COST;
return path;
}
/**
* Copy src Constrained Path into dst Constrained Path. A new Constrained Path
* structure is dynamically allocated if dst is NULL. If src is NULL, the
* function return the dst disregarding if it is NULL or not.
*
* @param dest Destination Constrained Path structure
* @param src Source Constrained Path structure
*
* @return Pointer to the destination Constrained Path structure
*/
static struct c_path *cpath_copy(struct c_path *dest, const struct c_path *src)
{
struct c_path *new_path;
if (!src)
return dest;
if (!dest) {
new_path = XCALLOC(MTYPE_PCA, sizeof(struct c_path));
} else {
new_path = dest;
if (dest->edges)
list_delete(&new_path->edges);
}
new_path->dst = src->dst;
new_path->weight = src->weight;
new_path->edges = list_dup(src->edges);
new_path->status = src->status;
return new_path;
}
/**
* Delete Constrained Path structure. Previous allocated memory is freed.
*
* @param path Constrained Path structure to be deleted
*/
static void cpath_del(struct c_path *path)
{
if (!path)
return;
if (path->edges)
list_delete(&path->edges);
XFREE(MTYPE_PCA, path);
path = NULL;
}
/**
* Replace the list of edges in the next Constrained Path by the list of edges
* in the current Constrained Path.
*
* @param next_path next Constrained Path structure
* @param cur_path current Constrained Path structure
*/
static void cpath_replace(struct c_path *next_path, struct c_path *cur_path)
{
if (next_path->edges)
list_delete(&next_path->edges);
next_path->edges = list_dup(cur_path->edges);
}
/**
* Create a new Visited Node structure from the provided Vertex. Structure is
* dynamically allocated.
*
* @param vertex Vertex structure
*
* @return Pointer to the new Visited Node structure
*/
static struct v_node *vnode_new(struct ls_vertex *vertex)
{
struct v_node *vnode;
if (!vertex)
return NULL;
vnode = XCALLOC(MTYPE_PCA, sizeof(struct v_node));
vnode->vertex = vertex;
vnode->key = vertex->key;
return vnode;
}
/**
* Delete Visited Node structure. Previous allocated memory is freed.
*
* @param vnode Visited Node structure to be deleted
*/
static void vnode_del(struct v_node *vnode)
{
if (!vnode)
return;
XFREE(MTYPE_PCA, vnode);
vnode = NULL;
}
/**
* Search Vertex in TED by IPv4 address. The function search vertex by browsing
* the subnets table. It allows to find not only vertex by router ID, but also
* vertex by interface IPv4 address.
*
* @param ted Traffic Engineering Database
* @param ipv4 IPv4 address
*
* @return Vertex if found, NULL otherwise
*/
static struct ls_vertex *get_vertex_by_ipv4(struct ls_ted *ted,
struct in_addr ipv4)
{
struct ls_subnet *subnet;
struct prefix p;
p.family = AF_INET;
p.u.prefix4 = ipv4;
frr_each (subnets, &ted->subnets, subnet) {
if (subnet->key.family != AF_INET)
continue;
p.prefixlen = subnet->key.prefixlen;
if (prefix_same(&subnet->key, &p))
return subnet->vertex;
}
return NULL;
}
/**
* Search Vertex in TED by IPv6 address. The function search vertex by browsing
* the subnets table. It allows to find not only vertex by router ID, but also
* vertex by interface IPv6 address.
*
* @param ted Traffic Engineering Database
* @param ipv6 IPv6 address
*
* @return Vertex if found, NULL otherwise
*/
static struct ls_vertex *get_vertex_by_ipv6(struct ls_ted *ted,
struct in6_addr ipv6)
{
struct ls_subnet *subnet;
struct prefix p;
p.family = AF_INET6;
p.u.prefix6 = ipv6;
frr_each (subnets, &ted->subnets, subnet) {
if (subnet->key.family != AF_INET6)
continue;
p.prefixlen = subnet->key.prefixlen;
if (prefix_cmp(&subnet->key, &p) == 0)
return subnet->vertex;
}
return NULL;
}
struct cspf *cspf_new(void)
{
struct cspf *algo;
/* Allocate New CSPF structure */
algo = XCALLOC(MTYPE_PCA, sizeof(struct cspf));
/* Initialize RB-Trees */
processed_init(&algo->processed);
visited_init(&algo->visited);
pqueue_init(&algo->pqueue);
algo->path = NULL;
algo->pdst = NULL;
return algo;
}
struct cspf *cspf_init(struct cspf *algo, const struct ls_vertex *src,
const struct ls_vertex *dst, struct constraints *csts)
{
struct cspf *new_algo;
struct c_path *psrc;
if (!csts)
return NULL;
if (!algo)
new_algo = cspf_new();
else
new_algo = algo;
/* Initialize Processed Path and Priority Queue with Src & Dst */
if (src) {
psrc = cpath_new(src->key);
psrc->weight = 0;
processed_add(&new_algo->processed, psrc);
pqueue_add(&new_algo->pqueue, psrc);
new_algo->path = psrc;
}
if (dst) {
new_algo->pdst = cpath_new(dst->key);
processed_add(&new_algo->processed, new_algo->pdst);
}
memcpy(&new_algo->csts, csts, sizeof(struct constraints));
return new_algo;
}
struct cspf *cspf_init_v4(struct cspf *algo, struct ls_ted *ted,
const struct in_addr src, const struct in_addr dst,
struct constraints *csts)
{
struct ls_vertex *vsrc;
struct ls_vertex *vdst;
struct cspf *new_algo;
/* Sanity Check */
if (!ted)
return algo;
if (!algo)
new_algo = cspf_new();
else
new_algo = algo;
/* Got Source and Destination Vertex from TED */
vsrc = get_vertex_by_ipv4(ted, src);
vdst = get_vertex_by_ipv4(ted, dst);
csts->family = AF_INET;
return cspf_init(new_algo, vsrc, vdst, csts);
}
struct cspf *cspf_init_v6(struct cspf *algo, struct ls_ted *ted,
const struct in6_addr src, const struct in6_addr dst,
struct constraints *csts)
{
struct ls_vertex *vsrc;
struct ls_vertex *vdst;
struct cspf *new_algo;
/* Sanity Check */
if (!ted)
return algo;
if (!algo)
new_algo = cspf_new();
else
new_algo = algo;
/* Got Source and Destination Vertex from TED */
vsrc = get_vertex_by_ipv6(ted, src);
vdst = get_vertex_by_ipv6(ted, dst);
csts->family = AF_INET6;
return cspf_init(new_algo, vsrc, vdst, csts);
}
void cspf_clean(struct cspf *algo)
{
struct c_path *path;
struct v_node *vnode;
if (!algo)
return;
/* Normally, Priority Queue is empty. Clean it in case of. */
if (pqueue_count(&algo->pqueue)) {
frr_each_safe (pqueue, &algo->pqueue, path) {
pqueue_del(&algo->pqueue, path);
}
}
/* Empty Processed Path tree and associated Path */
if (processed_count(&algo->processed)) {
frr_each_safe (processed, &algo->processed, path) {
processed_del(&algo->processed, path);
cpath_del(path);
}
}
/* Empty visited Vertex tree and associated Node */
if (visited_count(&algo->visited)) {
frr_each_safe (visited, &algo->visited, vnode) {
visited_del(&algo->visited, vnode);
vnode_del(vnode);
}
}
memset(&algo->csts, 0, sizeof(struct constraints));
algo->path = NULL;
algo->pdst = NULL;
}
void cspf_del(struct cspf *algo)
{
if (!algo)
return;
/* Empty Priority Queue and Processes Path */
cspf_clean(algo);
/* Then, reset Priority Queue, Processed Path and Visited RB-Tree */
pqueue_fini(&algo->pqueue);
processed_fini(&algo->processed);
visited_fini(&algo->visited);
XFREE(MTYPE_PCA, algo);
algo = NULL;
}
/**
* Prune Edge if constraints are not met by testing Edge Attributes against
* given constraints and cumulative cost of the given constrained path.
*
* @param path On-going Computed Path with cumulative cost constraints
* @param edge Edge to be validate against Constraints
* @param csts Constraints for this path
*
* @return True if Edge should be prune, false if Edge is valid
*/
static bool prune_edge(const struct c_path *path, const struct ls_edge *edge,
const struct constraints *csts)
{
struct ls_vertex *dst;
struct ls_attributes *attr;
/* Check that Path, Edge and Constraints are valid */
if (!path || !edge || !csts)
return true;
/* Check that Edge has a valid destination */
if (!edge->destination)
return true;
dst = edge->destination;
/* Check that Edge has valid attributes */
if (!edge->attributes)
return true;
attr = edge->attributes;
/* Check that Edge belongs to the requested Address Family and type */
if (csts->family == AF_INET) {
if (IPV4_NET0(attr->standard.local.s_addr))
return true;
if (csts->type == SR_TE)
if (!CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID) ||
!CHECK_FLAG(dst->node->flags, LS_NODE_SR))
return true;
}
if (csts->family == AF_INET6) {
if (IN6_IS_ADDR_UNSPECIFIED(&attr->standard.local6))
return true;
if (csts->type == SR_TE)
if (!CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID6) ||
!CHECK_FLAG(dst->node->flags, LS_NODE_SR))
return true;
}
/*
* Check that total cost, up to this edge, respects the initial
* constraints
*/
switch (csts->ctype) {
case CSPF_METRIC:
if (!CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
return true;
if ((attr->metric + path->weight) > csts->cost)
return true;
break;
case CSPF_TE_METRIC:
if (!CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
return true;
if ((attr->standard.te_metric + path->weight) > csts->cost)
return true;
break;
case CSPF_DELAY:
if (!CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
return true;
if ((attr->extended.delay + path->weight) > csts->cost)
return true;
break;
}
/* If specified, check that Edge meet Bandwidth constraint */
if (csts->bw > 0.0) {
if (attr->standard.max_bw < csts->bw ||
attr->standard.max_rsv_bw < csts->bw ||
attr->standard.unrsv_bw[csts->cos] < csts->bw)
return true;
}
/* All is fine. We can consider this Edge valid, so not to be prune */
return false;
}
/**
* Relax constraints of the current path up to the destination vertex of the
* provided Edge. This function progress in the network topology by validating
* the next vertex on the computed path. If Vertex has not already been visited,
* list of edges of the current path is augmented with this edge if the new cost
* is lower than prior path up to this vertex. Current path is re-inserted in
* the Priority Queue with its new cost i.e. current cost + edge cost.
*
* @param algo CSPF structure
* @param edge Next Edge to be added to the current computed path
*
* @return True if current path reach destination, false otherwise
*/
static bool relax_constraints(struct cspf *algo, struct ls_edge *edge)
{
struct c_path pkey = {};
struct c_path *next_path;
struct v_node vnode = {};
uint32_t total_cost = MAX_COST;
/* Verify that we have a current computed path */
if (!algo->path)
return false;
/* Verify if we have not visited the next Vertex to avoid loop */
vnode.key = edge->destination->key;
if (visited_member(&algo->visited, &vnode)) {
return false;
}
/*
* Get Next Computed Path from next vertex key
* or create a new one if it has not yet computed.
*/
pkey.dst = edge->destination->key;
next_path = processed_find(&algo->processed, &pkey);
if (!next_path) {
next_path = cpath_new(pkey.dst);
processed_add(&algo->processed, next_path);
}
/*
* Add or update the Computed Path in the Priority Queue if total cost
* is lower than cost associated to this next Vertex. This could occurs
* if we process a Vertex that as not yet been visited in the Graph
* or if we found a shortest path up to this Vertex.
*/
switch (algo->csts.ctype) {
case CSPF_METRIC:
total_cost = edge->attributes->metric + algo->path->weight;
break;
case CSPF_TE_METRIC:
total_cost = edge->attributes->standard.te_metric +
algo->path->weight;
break;
case CSPF_DELAY:
total_cost =
edge->attributes->extended.delay + algo->path->weight;
break;
default:
break;
}
if (total_cost < next_path->weight) {
/*
* It is not possible to directly update the q_path in the
* Priority Queue. Indeed, if we modify the path weight, the
* Priority Queue must be re-ordered. So, we need fist to remove
* the q_path if it is present in the Priority Queue, then,
* update the Path, in particular the Weight, and finally
* (re-)insert it in the Priority Queue.
*/
struct c_path *path;
frr_each_safe (pqueue, &algo->pqueue, path) {
if (path->dst == pkey.dst) {
pqueue_del(&algo->pqueue, path);
break;
}
}
next_path->weight = total_cost;
cpath_replace(next_path, algo->path);
listnode_add(next_path->edges, edge);
pqueue_add(&algo->pqueue, next_path);
}
/* Return True if we reach the destination */
return (next_path->dst == algo->pdst->dst);
}
struct c_path *compute_p2p_path(struct cspf *algo, struct ls_ted *ted)
{
struct listnode *node;
struct ls_vertex *vertex;
struct ls_edge *edge;
struct c_path *optim_path;
struct v_node *vnode;
uint32_t cur_cost;
optim_path = cpath_new(0xFFFFFFFFFFFFFFFF);
optim_path->status = FAILED;
/* Check that all is correctly initialized */
if (!algo)
return optim_path;
if (!algo->csts.ctype)
return optim_path;
if (!algo->pdst) {
optim_path->status = NO_DESTINATION;
return optim_path;
}
if (!algo->path) {
optim_path->status = NO_SOURCE;
return optim_path;
}
if (algo->pdst->dst == algo->path->dst) {
optim_path->status = SAME_SRC_DST;
return optim_path;
}
optim_path->dst = algo->pdst->dst;
optim_path->status = IN_PROGRESS;
/*
* Process all Connected Vertex until priority queue becomes empty.
* Connected Vertices are added into the priority queue when
* processing the next Connected Vertex: see relax_constraints()
*/
cur_cost = MAX_COST;
while (pqueue_count(&algo->pqueue) != 0) {
/* Got shortest current Path from the Priority Queue */
algo->path = pqueue_pop(&algo->pqueue);
/* Add destination Vertex of this path to the visited RB Tree */
vertex = ls_find_vertex_by_key(ted, algo->path->dst);
if (!vertex)
continue;
vnode = vnode_new(vertex);
visited_add(&algo->visited, vnode);
/* Process all outgoing links from this Vertex */
for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) {
/*
* Skip Connected Edges that must be prune i.e.
* Edges that not satisfy the given constraints,
* in particular the Bandwidth, TE Metric and Delay.
*/
if (prune_edge(algo->path, edge, &algo->csts))
continue;
/*
* Relax constraints and check if we got a shorter
* candidate path
*/
if (relax_constraints(algo, edge) &&
algo->pdst->weight < cur_cost) {
cur_cost = algo->pdst->weight;
cpath_copy(optim_path, algo->pdst);
optim_path->status = SUCCESS;
}
}
}
/*
* The priority queue is empty => all the possible (vertex, path)
* elements have been explored. The optim_path contains the optimal
* path if it exists. Otherwise an empty path with status failed is
* returned.
*/
if (optim_path->status == IN_PROGRESS ||
listcount(optim_path->edges) == 0)
optim_path->status = FAILED;
cspf_clean(algo);
return optim_path;
}

211
lib/cspf.h Normal file
View File

@ -0,0 +1,211 @@
/*
* Constraints Shortest Path First algorithms definition - cspf.h
*
* Author: Olivier Dugeon <olivier.dugeon@orange.com>
*
* Copyright (C) 2022 Orange http://www.orange.com
*
* This file is part of Free Range Routing (FRR).
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _FRR_CSPF_H_
#define _FRR_CSPF_H_
#include "typesafe.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* This file defines the different structure used for Path Computation with
* various constrained. Up to now, standard metric, TE metric, delay and
* bandwidth constraints are supported.
* All proposed algorithms used the same principle:
* - A pruning function that keeps only links that meet constraints
* - A priority Queue that keeps the shortest on-going computed path
* - A main loop over all vertices to find the shortest path
*/
#define MAX_COST 0xFFFFFFFF
/* Status of the path */
enum path_status {
FAILED = 0,
NO_SOURCE,
NO_DESTINATION,
SAME_SRC_DST,
IN_PROGRESS,
SUCCESS
};
enum path_type {RSVP_TE = 1, SR_TE, SRV6_TE};
enum metric_type {CSPF_METRIC = 1, CSPF_TE_METRIC, CSPF_DELAY};
/* Constrained metrics structure */
struct constraints {
uint32_t cost; /* total cost (metric) of the path */
enum metric_type ctype; /* Metric Type: standard, TE or Delay */
float bw; /* bandwidth of the path */
uint8_t cos; /* Class of Service of the path */
enum path_type type; /* RSVP-TE or SR-TE path */
uint8_t family; /* AF_INET or AF_INET6 address family */
};
/* Priority Queue for Constrained Path Computation */
PREDECL_RBTREE_NONUNIQ(pqueue);
/* Processed Path for Constrained Path Computation */
PREDECL_RBTREE_UNIQ(processed);
/* Constrained Path structure */
struct c_path {
struct pqueue_item q_itm; /* entry in the Priority Queue */
uint32_t weight; /* Weight to sort path in Priority Queue */
struct processed_item p_itm; /* entry in the Processed RB Tree */
uint64_t dst; /* Destination vertex key of this path */
struct list *edges; /* List of Edges that compose this path */
enum path_status status; /* status of the computed path */
};
macro_inline int q_cmp(const struct c_path *p1, const struct c_path *p2)
{
return numcmp(p1->weight, p2->weight);
}
DECLARE_RBTREE_NONUNIQ(pqueue, struct c_path, q_itm, q_cmp);
macro_inline int p_cmp(const struct c_path *p1, const struct c_path *p2)
{
return numcmp(p1->dst, p2->dst);
}
DECLARE_RBTREE_UNIQ(processed, struct c_path, p_itm, p_cmp);
/* List of visited node */
PREDECL_RBTREE_UNIQ(visited);
struct v_node {
struct visited_item item; /* entry in the Processed RB Tree */
uint64_t key;
struct ls_vertex *vertex;
};
macro_inline int v_cmp(const struct v_node *p1, const struct v_node *p2)
{
return numcmp(p1->key, p2->key);
}
DECLARE_RBTREE_UNIQ(visited, struct v_node, item, v_cmp);
/* Path Computation algorithms structure */
struct cspf {
struct pqueue_head pqueue; /* Priority Queue */
struct processed_head processed; /* Paths that have been processed */
struct visited_head visited; /* Vertices that have been visited */
struct constraints csts; /* Constraints of the path */
struct c_path *path; /* Current Computed Path */
struct c_path *pdst; /* Computed Path to the destination */
};
/**
* Create a new CSPF structure. Memory is dynamically allocated.
*
* @return pointer to the new cspf structure
*/
extern struct cspf *cspf_new(void);
/**
* Initialize CSPF structure prior to compute a constrained path. If CSPF
* structure is NULL, a new CSPF is dynamically allocated prior to the
* configuration itself.
*
* @param algo CSPF structure, may be null if a new CSPF must be created
* @param src Source vertex of the requested path
* @param dst Destination vertex of the requested path
* @param csts Constraints of the requested path
*
* @return pointer to the initialized CSPF structure
*/
extern struct cspf *cspf_init(struct cspf *algo, const struct ls_vertex *src,
const struct ls_vertex *dst,
struct constraints *csts);
/**
* Initialize CSPF structure prior to compute a constrained path. If CSPF
* structure is NULL, a new CSPF is dynamically allocated prior to the
* configuration itself. This function starts by searching source and
* destination vertices from the IPv4 addresses in the provided TED.
*
* @param algo CSPF structure, may be null if a new CSPF must be created
* @param ted Traffic Engineering Database
* @param src Source IPv4 address of the requested path
* @param dst Destination IPv4 address of the requested path
* @param csts Constraints of the requested path
*
* @return pointer to the initialized CSPF structure
*/
extern struct cspf *cspf_init_v4(struct cspf *algo, struct ls_ted *ted,
const struct in_addr src,
const struct in_addr dst,
struct constraints *csts);
/**
* Initialize CSPF structure prior to compute a constrained path. If CSPF
* structure is NULL, a new CSPF is dynamically allocated prior to the
* configuration itself. This function starts by searching source and
* destination vertices from the IPv6 addresses in the provided TED.
*
* @param algo CSPF structure, may be null if a new CSPF must be created
* @param ted Traffic Engineering Database
* @param src Source IPv6 address of the requested path
* @param dst Destination IPv6 address of the requested path
* @param csts Constraints of the requested path
*
* @return pointer to the initialized CSPF structure
*/
extern struct cspf *cspf_init_v6(struct cspf *algo, struct ls_ted *ted,
const struct in6_addr src,
const struct in6_addr dst,
struct constraints *csts);
/**
* Clean CSPF structure. Reset all internal list and priority queue for latter
* initialization of the CSPF structure and new path computation.
*
* @param algo CSPF structure
*/
extern void cspf_clean(struct cspf *algo);
/**
* Delete CSPF structure, internal list and priority queue.
*
* @param algo CSPF structure
*/
extern void cspf_del(struct cspf *algo);
/**
* Compute point-to-point constrained path. cspf_init() function must be call
* prior to call this function.
*
* @param algo CSPF structure
* @param ted Traffic Engineering Database
*
* @return Constrained Path with status to indicate computation success
*/
extern struct c_path *compute_p2p_path(struct cspf *algo, struct ls_ted *ted);
#ifdef __cplusplus
}
#endif
#endif /* _FRR_CSPF_H_ */

View File

@ -16,6 +16,7 @@ lib_libfrr_la_SOURCES = \
lib/command_lex.l \
lib/command_match.c \
lib/command_parse.y \
lib/cspf.c \
lib/csv.c \
lib/debug.c \
lib/defaults.c \
@ -182,6 +183,7 @@ pkginclude_HEADERS += \
lib/command_graph.h \
lib/command_match.h \
lib/compiler.h \
lib/cspf.h \
lib/csv.h \
lib/db.h \
lib/debug.h \