mirror of
https://git.proxmox.com/git/mirror_frr
synced 2026-01-08 11:05:34 +00:00
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:
parent
29abd4e319
commit
fd8e262ab7
196
doc/developer/cspf.rst
Normal file
196
doc/developer/cspf.rst
Normal 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
646
lib/cspf.c
Normal 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
211
lib/cspf.h
Normal 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_ */
|
||||
@ -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 \
|
||||
|
||||
Loading…
Reference in New Issue
Block a user