Merge pull request #7351 from opensourcerouting/feature/pathd

Add a new SR-TE policy management daemon and an optional PCEP module
This commit is contained in:
Olivier Dugeon 2020-12-18 20:28:22 +01:00 committed by GitHub
commit 065f7c7cc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 18577 additions and 12 deletions

View File

@ -158,6 +158,7 @@ include bfdd/subdir.am
include yang/subdir.am
include yang/libyang_plugins/subdir.am
include vrrpd/subdir.am
include pathd/subdir.am
include vtysh/subdir.am
include tests/subdir.am

View File

@ -560,6 +560,8 @@ AC_ARG_ENABLE([fabricd],
AS_HELP_STRING([--disable-fabricd], [do not build fabricd]))
AC_ARG_ENABLE([vrrpd],
AS_HELP_STRING([--disable-vrrpd], [do not build vrrpd]))
AC_ARG_ENABLE([pathd],
AS_HELP_STRING([--disable-pathd], [do not build pathd]))
AC_ARG_ENABLE([bgp-announce],
AS_HELP_STRING([--disable-bgp-announce], [turn off BGP route announcement]))
AC_ARG_ENABLE([bgp-vnc],
@ -625,6 +627,8 @@ AC_ARG_ENABLE([pcreposix],
AS_HELP_STRING([--enable-pcreposix], [enable using PCRE Posix libs for regex functions]))
AC_ARG_ENABLE([fpm],
AS_HELP_STRING([--enable-fpm], [enable Forwarding Plane Manager support]))
AC_ARG_ENABLE([pcep],
AS_HELP_STRING([--enable-pcep], [enable PCEP support for pathd]))
AC_ARG_ENABLE([systemd],
AS_HELP_STRING([--enable-systemd], [enable Systemd support]))
AC_ARG_ENABLE([werror],
@ -1675,6 +1679,14 @@ else
esac
fi
AS_IF([test "$enable_pathd" != "no"], [
AC_DEFINE([HAVE_PATHD], [1], [pathd])
])
AS_IF([test "$enable_pcep" != "no"], [
AC_DEFINE([HAVE_PATHD_PCEP], [1], [pathd-pcep])
])
if test "$ac_cv_lib_json_c_json_object_get" = "no" -a "$BFDD" = "bfdd"; then
AC_MSG_ERROR(["you must use json-c library to use bfdd"])
fi
@ -2467,6 +2479,18 @@ AM_CONDITIONAL([IRDP], [$IRDP])
AM_CONDITIONAL([FPM], [test "$enable_fpm" = "yes"])
AM_CONDITIONAL([HAVE_PROTOBUF], [test "$enable_protobuf" = "yes"])
AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3])
dnl PCEP plugin
AM_CONDITIONAL([HAVE_PATHD_PCEP], [test "$enable_pcep" = "yes"])
AS_IF([test "$enable_pcep" = "yes"], [
AC_CHECK_LIB([pcep_pcc], [initialize_pcc], [
PATHD_PCEP_LIBS="-lpcep_pcc"
],[
AC_MSG_ERROR([PCEP library libpcep_pcc not found])
])
AC_SUBST([PATHD_PCEP_LIBS])
])
dnl daemons
AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"])
AM_CONDITIONAL([ZEBRA], [test "$enable_zebra" != "no"])
@ -2489,6 +2513,7 @@ AM_CONDITIONAL([SHARPD], [test "$enable_sharpd" = "yes"])
AM_CONDITIONAL([STATICD], [test "$enable_staticd" != "no"])
AM_CONDITIONAL([FABRICD], [test "$enable_fabricd" != "no"])
AM_CONDITIONAL([VRRPD], [test "$enable_vrrpd" != "no"])
AM_CONDITIONAL([PATHD], [test "$enable_pathd" != "no"])
AC_CONFIG_FILES([Makefile],[
test "$enable_dev_build" = "yes" && makefile_devbuild="--dev-build"

View File

@ -18,3 +18,4 @@ FRRouting Developer's Guide
ospf
zebra
vtysh
pathd

View File

@ -0,0 +1,115 @@
PATHD Internals
===============
Architecture
------------
Overview
........
The pathd deamon manages the segment routing policies, it owns the data
structures representing them and can load modules that manipulate them like the
PCEP module. Its responsibility is to select a candidate path for each
configured policy and to install it into Zebra.
Zebra
.....
Zebra manages policies that are active or pending to be activated due to the
next hop not being available yet. In zebra, policy data structures and APIs are
defined in `zebra_srte.[hc]`.
The responsibilities of Zebra are:
- Store the policies' segment list.
- Install the policies when their next-hop is available.
- Notify other daemons of the status of the policies.
Adding and removing policies is done using the commands `ZEBRA_SR_POLICY_SET`
and `ZEBRA_SR_POLICY_DELETE` as parameter of the function `zebra_send_sr_policy`
all defined in `zclient.[hc]`.
If the first segment of the policy is an unknown label, it is kept until
notified by the mpls hooks `zebra_mpls_label_created`, and then it is installed.
To get notified when a policy status changes, a client can implement the
`sr_policy_notify_status` callback defined in `zclient.[hc]`.
For encoding/decoding the various data structures used to comunicate with zebra,
the following functions are available from `zclient.[hc]`:
`zapi_sr_policy_encode`, `zapi_sr_policy_decode` and
`zapi_sr_policy_notify_status_decode`.
Pathd
.....
The pathd daemon manages all the possible candidate paths for the segment
routing policies and selects the best one following the
`segment routing policy draft <https://tools.ietf.org/html/draft-ietf-spring-segment-routing-policy-06#section-2.9>`_.
It also supports loadable modules for handling dynamic candidate paths and the
creation of new policies and candidate paths at runtime.
The responsibilities of the pathd base daemon, not including any optional
modules, are:
- Store the policies and all the possible candidate paths for them.
- Select the best candidate path for each policy and send it to Zebra.
- Provide VTYSH configuration to set up policies and candidate paths.
- Provide a Northbound API to manipulate **configured** policies and candidate paths.
- Handle loadable modules for extending the functionality.
- Provide an API to the loadable module to manipulate policies and candidate paths.
Threading Model
---------------
The daemon runs completely inside the main thread using FRR event model, there
is no threading involved.
Source Code
-----------
Internal Data Structures
........................
The main data structures for policies and candidate paths are defined in
`pathd.h` and implemented in `pathd.c`.
When modifying these structures, either directly or through the functions
exported by `pathd.h`, nothing should be deleted/freed right away. The deletion
or modification flags must be set and when all the changes are done, the
function `srte_apply_changes` must be called. When called, a new candidate path
may be elected and sent to Zebra, and all the structures flagged as deleted
will be freed. In addition, a hook will be called so dynamic modules can perform
any required action when the elected candidate path changes.
Northbound API
..............
The northbound API is defined in `path_nb.[ch]` and implemented in
`path_nb_config.c` for configuration data and `path_nb_state.c` for operational
data.
Command Line Client
...................
The command-line client (VTYSH) is implemented in `path_cli.c`.
Interface with Zebra
....................
All the functions interfacing with Zebra are defined and implemented in
`path_zebra.[hc]`.
Loadable Module API
...................
For the time being, the API the loadable module uses is defined by `pathd.h`,
but in the future, it should be moved to a dedicated include file.

View File

@ -0,0 +1,193 @@
PCEP Module Internals
=====================
Introduction
------------
The PCEP module for the pathd daemon implements the PCEP protocol described in
:rfc:`5440` to update the policies and candidate paths.
The protocol encoding/decoding and the basic session management is handled by
the `pceplib external library 1.2 <https://github.com/volta-networks/pceplib/tree/devel-1.2>`_.
Together with pceplib, this module supports at least partially:
- :rfc:`5440`
Most of the protocol defined in the RFC is implemented.
All the messages can be parsed, but this was only tested in the context
of segment routing. Only a very small subset of metric types can be
configured, and there is a known issue with some Cisco routers not
following the IANA numbers for metrics.
- :rfc:`8231`
Support delegation of candidate path after performing the initial
computation request. If the PCE does not respond or cannot compute
a path, an empty candidate path is delegated to the PCE.
Only tested in the context of segment routing.
- :rfc:`8408`
Only used to comunicate the support for segment routing to the PCE.
- :rfc:`8664`
All the NAI types are implemented, but only the MPLS NAI are supported.
If the PCE provide segments that are not MPLS labels, the PCC will
return an error.
Note that pceplib supports more RFCs and drafts, see pceplib
`README <https://github.com/volta-networks/pceplib/blob/master/README.md>`_
for more details.
Architecture
------------
Overview
........
The module is separated into multiple layers:
- pathd interface
- command-line console
- controller
- PCC
- pceplib interface
The pathd interface handles all the interactions with the daemon API.
The command-line console handles all the VTYSH configuration commands.
The controller manages the multiple PCC connections and the interaction between
them and the daemon interface.
The PCC handles a single connection to a PCE through a pceplib session.
The pceplib interface abstracts the API of the pceplib.
.. figure:: ../figures/pcep_module_threading_overview.svg
Threading Model
---------------
The module requires multiple threads to cooperate:
- The main thread used by the pathd daemon.
- The controller pthread used to isolate the PCC from the main thread.
- The possible threads started in the pceplib library.
To ensure thread safety, all the controller and PCC state data structures can
only be read and modified in the controller thread, and all the global data
structures can only be read and modified in the main thread. Most of the
interactions between these threads are done through FRR timers and events.
The controller is the bridge between the two threads, all the functions that
**MUST** be called from the main thread start with the prefix `pcep_ctrl_` and
all the functions that **MUST** be called from the controller thread start
with the prefix `pcep_thread_`. When an asynchronous action must be taken in
a different thread, an FRR event is sent to the thread. If some synchronous
operation is needed, the calling thread will block and run a callback in the
other thread, there the result is **COPIED** and returned to the calling thread.
No function other than the controller functions defined for it should be called
from the main thread. The only exception being some utility functions from
`path_pcep_lib.[hc]`.
All the calls to pathd API functions **MUST** be performed in the main thread,
for that, the controller sends FRR events handled in function
`path_pcep.c:pcep_main_event_handler`.
For the same reason, the console client only runs in the main thread. It can
freely use the global variable, but **MUST** use controller's `pcep_ctrl_`
functions to interact with the PCCs.
Source Code
-----------
Generic Data Structures
.......................
The data structures are defined in multiple places, and where they are defined
dictates where they can be used.
The data structures defined in `path_pcep.h` can be used anywhere in the module.
Internally, throughout the module, the `struct path` data structure is used
to describe PCEP messages. It is a simplified flattened structure that can
represent multiple complex PCEP message types. The conversion from this
structure to the PCEP data structures used by pceplib is done in the pceplib
interface layer.
The data structures defined in `path_pcep_controller.h` should only be used
in `path_pcep_controller.c`. Even if a structure pointer is passed as a parameter
to functions defined in `path_pcep_pcc.h`, these should consider it as an opaque
data structure only used to call back controller functions.
The same applies to the structures defined in `path_pcep_pcc.h`, even if the
controller owns a reference to this data structure, it should never read or
modify it directly, it should be considered an opaque structure.
The global data structure can be accessed from the pathd interface layer
`path_pcep.c` and the command line client code `path_pcep_cli.c`.
Interface With Pathd
....................
All the functions calling or called by the pathd daemon are implemented in
`path_pcep.c`. These functions **MUST** run in the main FRR thread, and
all the interactions with the controller and the PCCs **MUST** pass through
the controller's `pcep_ctrl_` prefixed functions.
To handle asynchronous events from the PCCs, a callback is passed to
`pcep_ctrl_initialize` that is called in the FRR main thread context.
Command Line Client
...................
All the command line configuration commands (VTYSH) are implemented in
`path_pcep_cli.c`. All the functions there run in the main FRR thread and
can freely access the global variables. All the interaction with the
controller's and the PCCs **MUST** pass through the controller `pcep_ctrl_`
prefixed functions.
Debugging Helpers
.................
All the functions formating data structures for debugging and logging purposes
are implemented in `path_pcep_debug.[hc]`.
Interface with pceplib
......................
All the functions calling the pceplib external library are defined in
`path_pcep_lib.[hc]`. Some functions are called from the main FRR thread, like
`pcep_lib_initialize`, `pcep_lib_finalize`; some can be called from either
thread, like `pcep_lib_free_counters`; some function must be called from the
controller thread, like `pcep_lib_connect`. This will probably be formalized
later on with function prefix like done in the controller.
Controller
..........
The controller is defined and implemented in `path_pcep_controller.[hc]`.
Part of the controller code runs in FRR main thread and part runs in its own
FRR pthread started to isolate the main thread from the PCCs' event loop.
To communicate between the threads it uses FRR events, timers and
`thread_execute` calls.
PCC
...
Each PCC instance owns its state and runs in the controller thread. They are
defined and implemented in `path_pcep_pcc.[hc]`. All the interactions with
the daemon must pass through some controller's `pcep_thread_` prefixed function.

View File

@ -0,0 +1,11 @@
.. _path_internals:
*********
Internals
*********
.. toctree::
:maxdepth: 2
path-internals-daemon
path-internals-pcep

11
doc/developer/path.rst Normal file
View File

@ -0,0 +1,11 @@
.. _path:
*****
PATHD
*****
.. toctree::
:maxdepth: 2
path-internals

View File

@ -46,6 +46,10 @@ dev_RSTFILES = \
doc/developer/packaging-debian.rst \
doc/developer/packaging-redhat.rst \
doc/developer/packaging.rst \
doc/developer/path-internals-daemon.rst \
doc/developer/path-internals-pcep.rst \
doc/developer/path-internals.rst \
doc/developer/path.rst \
doc/developer/rcu.rst \
doc/developer/static-linking.rst \
doc/developer/testing.rst \

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -50,6 +50,7 @@ Protocols
nhrpd
ospfd
ospf6d
pathd
pim
pbr
ripd

443
doc/user/pathd.rst Normal file
View File

@ -0,0 +1,443 @@
.. _path:
****
PATH
****
:abbr:`PATH` is a daemon that handles the installation and deletion
of Segment Routing (SR) Policies.
.. _starting-path:
Starting PATH
=============
Default configuration file for *pathd* is :file:`pathd.conf`. The typical
location of :file:`pathd.conf` is |INSTALL_PREFIX_ETC|/pathd.conf.
If the user is using integrated config, then :file:`pathd.conf` need not be
present and the :file:`frr.conf` is read instead.
.. program:: pathd
:abbr:`PATH` supports all the common FRR daemon start options which are
documented elsewhere.
PCEP Support
============
To build the PCC for pathd, the externall library `pceplib 1.2 <https://github.com/volta-networks/pceplib/tree/devel-1.2>`_ is required.
To build FRR with support for PCEP the following steps must be followed:
- Checkout and build pceplib:
```
$ git clone https://github.com/volta-networks/pceplib
$ cd pceplib
$ make
$ make install
$ export PCEPLIB_ROOT=$PWD
```
- Configure FRR with the extra parameters:
```
--enable-pcep LDFLAGS="-L${PCEPLIB_ROOT}/install/lib" CPPFLAGS="-I${PCEPLIB_ROOT}/install/include"
```
To start pathd with pcep support the extra parameter `-M pathd_pcep` should be
passed to the pathd daemon.
Pathd Configuration
===================
Example:
.. code-block:: frr
debug pathd pcep basic
segment-routing
traffic-eng
segment-list SL1
index 10 mpls label 16010
index 20 mpls label 16030
!
policy color 1 endpoint 1.1.1.1
name default
binding-sid 4000
candidate-path preference 100 name CP1 explicit segment-list SL1
candidate-path preference 200 name CP2 dynamic
affinity include-any 0x000000FF
bandwidth 100000
metric bound msd 16 required
metric te 10
objective-function mcp required
!
pcep
pce-config GROUP1
source-address 1.1.1.1
tcp-md5-auth secret
timer keep-alive 30
!
pce PCE1
config GROUP1
address ip 10.10.10.10
!
pce PCE2
config GROUP1
address ip 9.9.9.9
!
pcc
peer PCE1 precedence 10
peer PCE2 precedence 20
!
!
!
!
.. _path-commands:
Configuration Commands
----------------------
.. index:: segment-routing
.. clicmd:: segment-routing
Configure segment routing.
.. index:: traffic-eng
.. clicmd:: traffic-eng
Configure segment routing traffic engineering.
.. index:: [no] segment-list NAME
.. clicmd:: [no] segment-list NAME
Delete or start a segment list definition.
.. index:: [no] index INDEX mpls label LABEL [nai node ADDRESS]
.. clicmd:: [no] index INDEX mpls label LABEL [nai node ADDRESS]
Delete or specify a segment in a segment list definition.
.. index:: [no] policy color COLOR endpoint ENDPOINT
.. clicmd:: [no] policy color COLOR endpoint ENDPOINT
Delete or start a policy definition.
.. index:: name NAME
.. clicmd:: name NAME
Specify the policy name.
.. index:: binding-sid LABEL
.. clicmd:: binding-sid LABEL
Specify the policy SID.
.. index:: [no] candidate-path preference PREFERENCE name NAME explicit segment-list SEGMENT-LIST-NAME
.. clicmd:: [no] candidate-path preference PREFERENCE name NAME explicit segment-list SEGMENT-LIST-NAME
Delete or define an explicit candidate path.
.. index:: [no] candidate-path preference PREFERENCE name NAME dynamic
.. clicmd:: [no] candidate-path preference PREFERENCE name NAME dynamic
Delete or start a dynamic candidate path definition.
.. index:: [no] affinity {exclude-any|include-any|include-all} BITPATTERN
.. clicmd:: [no] affinity {exclude-any|include-any|include-all} BITPATTERN
Delete or specify an affinity constraint for a dynamic candidate path.
.. index:: [no] bandwidth BANDWIDTH [required]
.. clicmd:: [no] bandwidth BANDWIDTH [required]
Delete or specify a bandwidth constraint for a dynamic candidate path.
.. index:: [no] metric [bound] METRIC VALUE [required]
.. clicmd:: [no] metric [bound] METRIC VALUE [required]
Delete or specify a metric constraint for a dynamic candidate path.
The possible metrics are:
- igp: IGP metric
- te: TE metric
- hc: Hop Counts
- abc: Aggregate bandwidth consumption
- mll: Load of the most loaded link
- igp: Cumulative IGP cost
- cte: Cumulative TE cost
- igp: P2MP IGP metric
- pte: P2MP TE metric
- phc: P2MP hop count metric
- msd: Segment-ID (SID) Depth
- pd: Path Delay metric
- pdv: Path Delay Variation metric
- pl: Path Loss metric
- ppd: P2MP Path Delay metric
- pdv: P2MP Path Delay variation metric
- ppl: P2MP Path Loss metric
- nap: Number of adaptations on a path
- nlp: Number of layers on a path
- dc: Domain Count metric
- bnc: Border Node Count metric
.. index:: [no] objective-function OBJFUN1 [required]
.. clicmd:: [no] objective-function OBJFUN1 [required]
Delete or specify a PCEP objective function constraint for a dynamic
candidate path.
The possible functions are:
- mcp: Minimum Cost Path [RFC5541]
- mlp: Minimum Load Path [RFC5541]
- mbp: Maximum residual Bandwidth Path [RFC5541]
- mbc: Minimize aggregate Bandwidth Consumption [RFC5541]
- mll: Minimize the Load of the most loaded Link [RFC5541]
- mcc: Minimize the Cumulative Cost of a set of paths [RFC5541]
- spt: Shortest Path Tree [RFC8306]
- mct: Minimum Cost Tree [RFC8306]
- mplp: Minimum Packet Loss Path [RFC8233]
- mup: Maximum Under-Utilized Path [RFC8233]
- mrup: Maximum Reserved Under-Utilized Path [RFC8233]
- mtd: Minimize the number of Transit Domains [RFC8685]
- mbn: Minimize the number of Border Nodes [RFC8685]
- mctd: Minimize the number of Common Transit Domains [RFC8685]
- msl: Minimize the number of Shared Links [RFC8800]
- mss: Minimize the number of Shared SRLGs [RFC8800]
- msn: Minimize the number of Shared Nodes [RFC8800]
.. index:: [no] debug pathd pcep [basic|path|message|pceplib]
.. clicmd:: [no] debug pathd pcep [basic|path|message|pceplib]
Enable or disable debugging for the pcep module:
- basic: Enable basic PCEP logging
- path: Log the path structures
- message: Log the PCEP messages
- pceplib: Enable pceplib logging
.. index:: pcep
.. clicmd:: pcep
Configure PCEP support.
.. index:: [no] cep-config NAME
.. clicmd:: [no] pce-config NAME
Define a shared PCE configuration that can be used in multiple PCE
declarations.
.. index:: [no] pce NAME
.. clicmd:: [no] pce NAME
Define or delete a PCE definition.
.. index:: config WORD
.. clicmd:: config WORD
Select a shared configuration. If not defined, the default
configuration will be used.
.. index:: address <ip A.B.C.D | ipv6 X:X::X:X> [port (1024-65535)]
.. clicmd:: address <ip A.B.C.D | ipv6 X:X::X:X> [port (1024-65535)]
Define the address and port of the PCE.
If not specified, the port is the standard PCEP port 4189.
This should be specified in the PCC peer definition.
.. index:: source-address [ip A.B.C.D | ipv6 X:X::X:X] [port PORT]
.. clicmd:: source-address [ip A.B.C.D | ipv6 X:X::X:X] [port PORT]
Define the address and/or port of the PCC as seen by the PCE.
This can be used in a configuration group or a PCC peer declaration.
If not specified, the source address will be the router identifier selected
by zebra, and the port will be the standard PCEP port 4189.
This can be specified in either the PCC peer definition or in a
configuration group.
.. index:: tcp-md5-auth WORD
.. clicmd:: tcp-md5-auth WORD
Enable TCP MD5 security with the given secret.
This can be specified in either the PCC peer definition or in a
configuration group.
.. index:: sr-draft07
.. clicmd:: sr-draft07
Specify if a PCE only support segment routing draft 7, this flag will limit
the PCC behavior to this draft.
This can be specified in either the PCC peer definition or in a
configuration group.
.. index:: pce-initiated
.. clicmd:: pce-initiated
Specify if PCE-initiated LSP should be allowed for this PCE.
This can be specified in either the PCC peer definition or in a
configuration group.
.. index:: timer [keep-alive (1-63)] [min-peer-keep-alive (1-255)] [max-peer-keep-alive (1-255)] [dead-timer (4-255)] [min-peer-dead-timer (4-255)] [max-peer-dead-timer (4-255)] [pcep-request (1-120)] [session-timeout-interval (1-120)] [delegation-timeout (1-60)]
.. clicmd:: timer [keep-alive (1-63)] [min-peer-keep-alive (1-255)] [max-peer-keep-alive (1-255)] [dead-timer (4-255)] [min-peer-dead-timer (4-255)] [max-peer-dead-timer (4-255)] [pcep-request (1-120)] [session-timeout-interval (1-120)] [delegation-timeout (1-60)]
Specify the PCEP timers.
This can be specified in either the PCC peer definition or in a
configuration group.
.. index:: [no] pcc
.. clicmd:: [no] pcc
Disable or start the definition of a PCC.
.. index:: msd (1-32)
.. clicmd:: msd (1-32)
Specify the maximum SID depth in a PCC definition.
.. index:: [no] peer WORD [precedence (1-255)]
.. clicmd:: [no] peer WORD [precedence (1-255)]
Specify a peer and its precedence in a PCC definition.
Introspection Commands
----------------------
.. index:: show sr-te policy [detail]
.. clicmd:: show sr-te policy [detail]
Display the segment routing policies.
.. code-block:: frr
router# show sr-te policy
Endpoint Color Name BSID Status
------------------------------------------
1.1.1.1 1 default 4000 Active
.. code-block:: frr
router# show sr-te policy detail
Endpoint: 1.1.1.1 Color: 1 Name: LOW_DELAY BSID: 4000 Status: Active
Preference: 100 Name: cand1 Type: explicit Segment-List: sl1 Protocol-Origin: Local
* Preference: 200 Name: cand1 Type: dynamic Segment-List: 32453452 Protocol-Origin: PCEP
The asterisk (*) marks the best, e.g. active, candidate path. Note that for segment-lists which are
retrieved via PCEP a random number based name is generated.
.. index:: show debugging pathd
.. clicmd:: show debugging pathd
Display the current status of the pathd debugging.
.. index:: show debugging pathd-pcep
.. clicmd:: show debugging pathd-pcep
Display the current status of the pcep module debugging.
.. index:: show sr-te pcep counters
.. clicmd:: show sr-te pcep counters
Display the counters from pceplib.
.. index:: show sr-te pcep pce-config [NAME]
.. clicmd:: show sr-te pcep pce-config [NAME]
Display a shared configuration. if no name is specified, the default
configuration will be displayed.
.. index:: show sr-te pcep pcc
.. clicmd:: show sr-te pcep pcc
Display PCC information.
.. index:: show sr-te pcep session [NAME]
.. clicmd:: show sr-te pcep session [NAME]
Display the information of a PCEP session, if not name is specified all the
sessions will be displayed.
Utility Commands
----------------
.. index:: clear sr-te pcep session [NAME]
.. clicmd:: clear sr-te pcep session [NAME]
Reset the pcep session by disconnecting from the PCE and performing the
normal reconnection process. No configuration is changed.
Usage with BGP route-maps
=========================
It is possible to steer traffic 'into' a segment routing policy for routes
learned through BGP using route-maps:
.. code-block:: frr
route-map SET_SR_POLICY permit 10
set sr-te color 1
!
router bgp 1
bgp router-id 2.2.2.2
neighbor 1.1.1.1 remote-as 1
neighbor 1.1.1.1 update-source lo
!
address-family ipv4 unicast
neighbor 1.1.1.1 next-hop-self
neighbor 1.1.1.1 route-map SET_SR_POLICY in
redistribute static
exit-address-family
!
!
In this case, the SR Policy with color `1` and endpoint `1.1.1.1` is selected.

View File

@ -27,6 +27,7 @@ user_RSTFILES = \
doc/user/ospf_fundamentals.rst \
doc/user/overview.rst \
doc/user/packet-dumps.rst \
doc/user/pathd.rst \
doc/user/pim.rst \
doc/user/ripd.rst \
doc/user/pbr.rst \

View File

@ -863,6 +863,30 @@ enum node_type node_parent(enum node_type node)
case BFD_PROFILE_NODE:
ret = BFD_NODE;
break;
case SR_TRAFFIC_ENG_NODE:
ret = SEGMENT_ROUTING_NODE;
break;
case SR_SEGMENT_LIST_NODE:
ret = SR_TRAFFIC_ENG_NODE;
break;
case SR_POLICY_NODE:
ret = SR_TRAFFIC_ENG_NODE;
break;
case SR_CANDIDATE_DYN_NODE:
ret = SR_POLICY_NODE;
break;
case PCEP_NODE:
ret = SR_TRAFFIC_ENG_NODE;
break;
case PCEP_PCE_CONFIG_NODE:
ret = PCEP_NODE;
break;
case PCEP_PCE_NODE:
ret = PCEP_NODE;
break;
case PCEP_PCC_NODE:
ret = PCEP_NODE;
break;
default:
ret = CONFIG_NODE;
break;

View File

@ -145,6 +145,15 @@ enum node_type {
PROTOCOL_NODE, /* protocol filtering node */
MPLS_NODE, /* MPLS config node */
PW_NODE, /* Pseudowire config node */
SEGMENT_ROUTING_NODE, /* Segment routing root node */
SR_TRAFFIC_ENG_NODE, /* SR Traffic Engineering node */
SR_SEGMENT_LIST_NODE, /* SR segment list config node */
SR_POLICY_NODE, /* SR policy config node */
SR_CANDIDATE_DYN_NODE, /* SR dynamic candidate path config node */
PCEP_NODE, /* PCEP node */
PCEP_PCE_CONFIG_NODE, /* PCE shared configuration node */
PCEP_PCE_NODE, /* PCE configuration node */
PCEP_PCC_NODE, /* PCC configuration node */
VTY_NODE, /* Vty node. */
FPM_NODE, /* Dataplane FPM node. */
LINK_PARAMS_NODE, /* Link-parameters node */

View File

@ -132,6 +132,8 @@ struct ferr {
#define VTYSH_FRR_END 0x0FFFFFFF
#define WATCHFRR_FERR_START 0x10000001
#define WATCHFRR_FERR_END 0x10FFFFFF
#define PATH_FERR_START 0x11000001
#define PATH_FERR_END 0x11FFFFFF
#define ZEBRA_FERR_START 0xF1000001
#define ZEBRA_FERR_END 0xF1FFFFFF
#define END_FERR 0xFFFFFFFF

View File

@ -600,6 +600,7 @@ enum nb_client {
NB_CLIENT_CONFD,
NB_CLIENT_SYSREPO,
NB_CLIENT_GRPC,
NB_CLIENT_PCEP,
};
/* Northbound context. */
@ -621,6 +622,8 @@ struct nb_context {
} sysrepo;
struct {
} grpc;
struct {
} pcep;
} client_data;
#endif
};

View File

@ -92,6 +92,7 @@ static const char *const frr_native_modules[] = {
"frr-isisd",
"frr-vrrpd",
"frr-zebra",
"frr-pathd",
};
/* clang-format on */

2
pathd/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
libpath.a
pathd

10
pathd/Makefile Normal file
View File

@ -0,0 +1,10 @@
all: ALWAYS
@$(MAKE) -s -C .. pathd/pathd
%: ALWAYS
@$(MAKE) -s -C .. pathd/$@
Makefile:
#nothing
ALWAYS:
.PHONY: ALWAYS makefiles
.SUFFIXES:

1108
pathd/path_cli.c Normal file

File diff suppressed because it is too large Load Diff

121
pathd/path_debug.c Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <libyang/libyang.h>
#include "printfrr.h"
#include "ipaddr.h"
#include "pathd/path_debug.h"
THREAD_DATA char _debug_buff[DEBUG_BUFF_SIZE];
/**
* Gives the string representation of an srte_protocol_origin enum value.
*
* @param origin The enum value to convert to string
* @return a constant string representation of the enum value
*/
const char *srte_protocol_origin_name(enum srte_protocol_origin origin)
{
switch (origin) {
case SRTE_ORIGIN_UNDEFINED:
return "UNDEFINED";
case SRTE_ORIGIN_PCEP:
return "PCEP";
case SRTE_ORIGIN_BGP:
return "BGP";
case SRTE_ORIGIN_LOCAL:
return "LOCAL";
default:
return "UNKNOWN";
}
}
/**
* Gives the string representation of an srte_candidate_type enum value.
*
* @param origin The enum value to convert to string
* @return a constant string representation of the enum value
*/
const char *srte_candidate_type_name(enum srte_candidate_type type)
{
switch (type) {
case SRTE_CANDIDATE_TYPE_EXPLICIT:
return "EXPLICIT";
case SRTE_CANDIDATE_TYPE_DYNAMIC:
return "DYNAMIC";
case SRTE_CANDIDATE_TYPE_UNDEFINED:
return "UNDEFINED";
default:
return "UNKNOWN";
}
}
/**
* Gives the string representation of an objfun_type enum value.
*
* @param origin The enum value to convert to string
* @return a constant string representation of the enum value
*/
const char *objfun_type_name(enum objfun_type type)
{
switch (type) {
case OBJFUN_UNDEFINED:
return "UNDEFINED";
case OBJFUN_MCP:
return "MCP";
case OBJFUN_MLP:
return "MLP";
case OBJFUN_MBP:
return "MBP";
case OBJFUN_MBC:
return "MBC";
case OBJFUN_MLL:
return "MLL";
case OBJFUN_MCC:
return "MCC";
case OBJFUN_SPT:
return "SPT";
case OBJFUN_MCT:
return "MCT";
case OBJFUN_MPLP:
return "MPLP";
case OBJFUN_MUP:
return "MUP";
case OBJFUN_MRUP:
return "MRUP";
case OBJFUN_MTD:
return "MTD";
case OBJFUN_MBN:
return "MBN";
case OBJFUN_MCTD:
return "MCTD";
case OBJFUN_MSL:
return "MSL";
case OBJFUN_MSS:
return "MSS";
case OBJFUN_MSN:
return "MSN";
default:
return "UNKNOWN";
}
}

44
pathd/path_debug.h Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PATH_DEBUG_H_
#define _PATH_DEBUG_H_
#include "pathd/pathd.h"
#ifdef __GNUC__
#define THREAD_DATA __thread
#else
#define THREAD_DATA
#endif
#define DEBUG_IDENT_SIZE 4
#define DEBUG_BUFF_SIZE 4096
#define TUP(A, B) ((((uint32_t)(A)) << 16) | ((uint32_t)(B)))
#define PATHD_FORMAT_INIT() _debug_buff[0] = 0
#define PATHD_FORMAT(fmt, ...) \
csnprintfrr(_debug_buff, DEBUG_BUFF_SIZE, fmt, ##__VA_ARGS__)
#define PATHD_FORMAT_FINI() _debug_buff
extern THREAD_DATA char _debug_buff[DEBUG_BUFF_SIZE];
const char *srte_protocol_origin_name(enum srte_protocol_origin origin);
const char *srte_candidate_type_name(enum srte_candidate_type type);
const char *objfun_type_name(enum objfun_type type);
#endif // _PATH_DEBUG_H_

134
pathd/path_errors.c Normal file
View File

@ -0,0 +1,134 @@
/*
* pathd-specific error messages.
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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 "lib/ferr.h"
#include "path_errors.h"
/* clang-format off */
static struct log_ref ferr_path_err[] = {
{
.code = EC_PATH_SYSTEM_CALL,
.title = "Thread setup error",
.description = "A system call for creating, or setting up PCEP module's pthread failed",
.suggestion = "Open an Issue with all relevant log files and restart FRR"
},
{
.code = EC_PATH_PCEP_PCC_INIT,
.title = "PCC initialization error",
.description = "pceplib PCC initialization call failed",
.suggestion = "Open an Issue with all relevant log files and restart FRR"
},
{
.code = EC_PATH_PCEP_PCC_FINI,
.title = "PCC finalization error",
.description = "pceplib PCC finalization call failed",
.suggestion = "Open an Issue with all relevant log files and restart FRR"
},
{
.code = EC_PATH_PCEP_PCC_CONF_UPDATE,
.title = "PCC configuration update error",
.description = "The update of the PCC configuration failed",
.suggestion = "Open an Issue with all relevant log files and restart FRR"
},
{
.code = END_FERR,
}
};
static struct log_ref ferr_path_warn[] = {
{
.code = EC_PATH_PCEP_LIB_CONNECT,
.title = "PCC connection error",
.description = "The PCEP module failed to connected to configured PCE",
.suggestion = "Check the connectivity between the PCC and the PCE"
},
{
.code = EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
.title = "PCC connection error",
.description = "The PCEP module did not try to connect because it is missing a source address",
.suggestion = "Wait for the router ID to be defined or set the PCC source address in the configuration"
},
{
.code = EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
.title = "Recoverable internal error",
.description = "Some recoverable internal error",
.suggestion = "Open an Issue with all relevant log files"
},
{
.code = EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
.title = "Unsupported PCEP feature",
.description = "Receved an unsupported PCEP message",
.suggestion = "The PCC and PCE are probably not compatible. Open an Issue with all relevant log files"
},
{
.code = EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE,
.title = "Unexpected PCEP message",
.description = "The PCEP module received an unexpected PCEP message",
.suggestion = "Open an Issue with all relevant log files"
},
{
.code = EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT,
.title = "Unexpected pceplib event",
.description = "The PCEP module received an unexpected event from pceplib",
.suggestion = "Open an Issue with all relevant log files"
},
{
.code = EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
.title = "Unexpected PCEP object",
.description = "The PCEP module received an unexpected PCEP object from a PCE",
.suggestion = "Open an Issue with all relevant log files"
},
{
.code = EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
.title = "Unexpected PCEP TLV",
.description = "The PCEP module received an unexpected PCEP TLV from a PCE",
.suggestion = "Open an Issue with all relevant log files"
},
{
.code = EC_PATH_PCEP_UNEXPECTED_PCEP_ERO_SUBOBJ,
.title = "Unexpected PCEP ERO sub-object",
.description = "The PCEP module received an unexpected PCEP ERO sub-object from a PCE",
.suggestion = "Open an Issue with all relevant log files"
},
{
.code = EC_PATH_PCEP_UNEXPECTED_SR_NAI,
.title = "Unexpected PCEP SR segment NAI",
.description = "The PCEP module received an SR segment with an unsupported NAI specification from the PCE",
.suggestion = "Open an Issue with all relevant log files"
},
{
.code = EC_PATH_PCEP_COMPUTATION_REQUEST_TIMEOUT,
.title = "Computation request timeout",
.description = "The PCE did not respond in time to the PCC computation request",
.suggestion = "The PCE is overloaded or incompatible with the PCC, try with a different PCE"
},
{
.code = END_FERR,
}
};
/* clang-format on */
void path_error_init(void)
{
log_ref_add(ferr_path_err);
log_ref_add(ferr_path_warn);
}

45
pathd/path_errors.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __PATH_ERRORS_H__
#define __PATH_ERRORS_H__
#include "lib/ferr.h"
enum path_log_refs {
EC_PATH_PCEP_INIT = PATH_FERR_START,
EC_PATH_SYSTEM_CALL,
EC_PATH_PCEP_PCC_INIT,
EC_PATH_PCEP_PCC_FINI,
EC_PATH_PCEP_PCC_CONF_UPDATE,
EC_PATH_PCEP_LIB_CONNECT,
EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE,
EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT,
EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
EC_PATH_PCEP_UNEXPECTED_PCEP_ERO_SUBOBJ,
EC_PATH_PCEP_UNEXPECTED_SR_NAI,
EC_PATH_PCEP_COMPUTATION_REQUEST_TIMEOUT
};
extern void path_error_init(void);
#endif

154
pathd/path_main.c Normal file
View File

@ -0,0 +1,154 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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 <lib/version.h>
#include "getopt.h"
#include "thread.h"
#include "command.h"
#include "log.h"
#include "memory.h"
#include "privs.h"
#include "sigevent.h"
#include "libfrr.h"
#include "vrf.h"
#include "filter.h"
#include "pathd.h"
#include "path_nb.h"
#include "path_zebra.h"
#include "path_errors.h"
char backup_config_file[256];
zebra_capabilities_t _caps_p[] = {};
struct zebra_privs_t pathd_privs = {
#if defined(FRR_USER) && defined(FRR_GROUP)
.user = FRR_USER,
.group = FRR_GROUP,
#endif
#if defined(VTY_GROUP)
.vty_group = VTY_GROUP,
#endif
.caps_p = _caps_p,
.cap_num_p = array_size(_caps_p),
.cap_num_i = 0};
struct option longopts[] = {{0}};
/* Master of threads. */
struct thread_master *master;
static struct frr_daemon_info pathd_di;
/* SIGHUP handler. */
static void sighup(void)
{
zlog_info("SIGHUP received");
/* Reload config file. */
vty_read_config(NULL, pathd_di.config_file, config_default);
}
/* SIGINT / SIGTERM handler. */
static void sigint(void)
{
zlog_notice("Terminating on signal");
exit(0);
}
/* SIGUSR1 handler. */
static void sigusr1(void)
{
zlog_rotate();
}
struct quagga_signal_t path_signals[] = {
{
.signal = SIGHUP,
.handler = &sighup,
},
{
.signal = SIGUSR1,
.handler = &sigusr1,
},
{
.signal = SIGINT,
.handler = &sigint,
},
{
.signal = SIGTERM,
.handler = &sigint,
},
};
static const struct frr_yang_module_info *pathd_yang_modules[] = {
&frr_filter_info,
&frr_interface_info,
&frr_pathd_info,
};
#define PATH_VTY_PORT 2621
FRR_DAEMON_INFO(pathd, PATH, .vty_port = PATH_VTY_PORT,
.proghelp = "Implementation of PATH.",
.signals = path_signals, .n_signals = array_size(path_signals),
.privs = &pathd_privs, .yang_modules = pathd_yang_modules,
.n_yang_modules = array_size(pathd_yang_modules), )
int main(int argc, char **argv, char **envp)
{
frr_preinit(&pathd_di, argc, argv);
frr_opt_add("", longopts, "");
while (1) {
int opt;
opt = frr_getopt(argc, argv, NULL);
if (opt == EOF)
break;
switch (opt) {
case 0:
break;
default:
frr_help_exit(1);
break;
}
}
master = frr_init();
access_list_init();
path_error_init();
path_zebra_init(master);
path_cli_init();
frr_config_fork();
frr_run(master);
/* Not reached. */
return 0;
}

25
pathd/path_memory.c Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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 <memory.h>
#include "pathd/path_memory.h"
DEFINE_MGROUP(PATHD, "pathd")

26
pathd/path_memory.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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_PATH_MEMORY_H_
#define _FRR_PATH_MEMORY_H_
#include "memory.h"
DECLARE_MGROUP(PATHD)
#endif /* _FRR_PATH_MEMORY_H_ */

351
pathd/path_nb.c Normal file
View File

@ -0,0 +1,351 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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 "northbound.h"
#include "libfrr.h"
#include "pathd/path_nb.h"
static int iter_objfun_cb(const struct lyd_node *dnode, void *arg);
static int dummy_create(struct nb_cb_create_args *args);
static int dummy_modify(struct nb_cb_modify_args *args);
static int dummy_destroy(struct nb_cb_destroy_args *args);
struct of_cb_pref {
uint32_t index;
enum objfun_type type;
struct of_cb_pref *next;
};
struct of_cb_args {
struct of_cb_pref *first;
uint32_t free_slot;
struct of_cb_pref prefs[MAX_OBJFUN_TYPE];
};
/* clang-format off */
const struct frr_yang_module_info frr_pathd_info = {
.name = "frr-pathd",
.nodes = {
{
.xpath = "/frr-pathd:pathd",
.cbs = {
.apply_finish = pathd_apply_finish,
},
.priority = NB_DFLT_PRIORITY + 1
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list",
.cbs = {
.create = pathd_srte_segment_list_create,
.cli_show = cli_show_srte_segment_list,
.destroy = pathd_srte_segment_list_destroy,
.get_next = pathd_srte_segment_list_get_next,
.get_keys = pathd_srte_segment_list_get_keys,
.lookup_entry = pathd_srte_segment_list_lookup_entry,
},
.priority = NB_DFLT_PRIORITY - 1
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/protocol-origin",
.cbs = {
.modify = pathd_srte_segment_list_protocol_origin_modify,
},
.priority = NB_DFLT_PRIORITY - 1
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/originator",
.cbs = {
.modify = pathd_srte_segment_list_originator_modify,
},
.priority = NB_DFLT_PRIORITY - 1
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment",
.cbs = {
.create = pathd_srte_segment_list_segment_create,
.cli_show = cli_show_srte_segment_list_segment,
.destroy = pathd_srte_segment_list_segment_destroy,
},
.priority = NB_DFLT_PRIORITY - 1
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/sid-value",
.cbs = {
.modify = pathd_srte_segment_list_segment_sid_value_modify,
},
.priority = NB_DFLT_PRIORITY - 1
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai",
.cbs = {
.create = dummy_create,
.destroy = pathd_srte_segment_list_segment_nai_destroy,
.apply_finish = pathd_srte_segment_list_segment_nai_apply_finish
},
.priority = NB_DFLT_PRIORITY - 1
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/type",
.cbs = {.modify = dummy_modify}
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-address",
.cbs = {.modify = dummy_modify}
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-interface",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-address",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-interface",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/policy",
.cbs = {
.create = pathd_srte_policy_create,
.cli_show = cli_show_srte_policy,
.destroy = pathd_srte_policy_destroy,
.get_next = pathd_srte_policy_get_next,
.get_keys = pathd_srte_policy_get_keys,
.lookup_entry = pathd_srte_policy_lookup_entry,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/name",
.cbs = {
.modify = pathd_srte_policy_name_modify,
.cli_show = cli_show_srte_policy_name,
.destroy = pathd_srte_policy_name_destroy,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/binding-sid",
.cbs = {
.modify = pathd_srte_policy_binding_sid_modify,
.cli_show = cli_show_srte_policy_binding_sid,
.destroy = pathd_srte_policy_binding_sid_destroy,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/is-operational",
.cbs = {
.get_elem = pathd_srte_policy_is_operational_get_elem
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path",
.cbs = {
.create = pathd_srte_policy_candidate_path_create,
.cli_show = cli_show_srte_policy_candidate_path,
.destroy = pathd_srte_policy_candidate_path_destroy,
.get_next = pathd_srte_policy_candidate_path_get_next,
.get_keys = pathd_srte_policy_candidate_path_get_keys,
.lookup_entry = pathd_srte_policy_candidate_path_lookup_entry,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/name",
.cbs = {
.modify = pathd_srte_policy_candidate_path_name_modify,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/is-best-candidate-path",
.cbs = {
.get_elem = pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/protocol-origin",
.cbs = {
.modify = pathd_srte_policy_candidate_path_protocol_origin_modify,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/originator",
.cbs = {
.modify = pathd_srte_policy_candidate_path_originator_modify,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/discriminator",
.cbs = {
.get_elem = pathd_srte_policy_candidate_path_discriminator_get_elem,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/type",
.cbs = {
.modify = pathd_srte_policy_candidate_path_type_modify,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/segment-list-name",
.cbs = {
.destroy = pathd_srte_policy_candidate_path_segment_list_name_destroy,
.modify = pathd_srte_policy_candidate_path_segment_list_name_modify,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth",
.cbs = {
.create = dummy_create,
.destroy = pathd_srte_policy_candidate_path_bandwidth_destroy,
.apply_finish = pathd_srte_policy_candidate_path_bandwidth_apply_finish
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth/required",
.cbs = {.modify = dummy_modify}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth/value",
.cbs = {.modify = dummy_modify}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/exclude-any",
.cbs = {
.modify = pathd_srte_policy_candidate_path_exclude_any_modify,
.destroy = pathd_srte_policy_candidate_path_exclude_any_destroy,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-any",
.cbs = {
.modify = pathd_srte_policy_candidate_path_include_any_modify,
.destroy = pathd_srte_policy_candidate_path_include_any_destroy,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-all",
.cbs = {
.modify = pathd_srte_policy_candidate_path_include_all_modify,
.destroy = pathd_srte_policy_candidate_path_include_all_destroy,
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics",
.cbs = {
.create = dummy_create,
.destroy = pathd_srte_policy_candidate_path_metrics_destroy,
.apply_finish = pathd_srte_policy_candidate_path_metrics_apply_finish
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/value",
.cbs = {.modify = dummy_modify}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/required",
.cbs = {.modify = dummy_modify}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/is-bound",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/is-computed",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function",
.cbs = {
.create = dummy_create,
.destroy = pathd_srte_policy_candidate_path_objfun_destroy,
.apply_finish = pathd_srte_policy_candidate_path_objfun_apply_finish
}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function/required",
.cbs = {.modify = dummy_modify}
},
{
.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function/type",
.cbs = {.modify = dummy_modify}
},
{
.xpath = NULL,
},
}
};
void iter_objfun_prefs(const struct lyd_node *dnode, const char* path,
of_pref_cp_t fun, void *arg)
{
struct of_cb_args args = {0};
struct of_cb_pref *p;
yang_dnode_iterate(iter_objfun_cb, &args, dnode, path);
for (p = args.first; p != NULL; p = p->next)
fun(p->type, arg);
}
int iter_objfun_cb(const struct lyd_node *dnode, void *arg)
{
struct of_cb_args *of_arg = arg;
struct of_cb_pref *pref;
struct of_cb_pref **p;
if (of_arg->free_slot >= MAX_OBJFUN_TYPE)
return YANG_ITER_STOP;
pref = &of_arg->prefs[of_arg->free_slot++];
pref->index = yang_dnode_get_uint32(dnode, "./index");
pref->type = yang_dnode_get_enum(dnode, "./type");
/* Simplistic insertion sort */
p = &of_arg->first;
while (true) {
if (*p == NULL) {
*p = pref;
break;
}
if ((*p)->index >= pref->index) {
pref->next = *p;
*p = pref;
break;
}
p = &(*p)->next;
}
return YANG_ITER_CONTINUE;
}
int dummy_create(struct nb_cb_create_args *args)
{
return NB_OK;
}
int dummy_modify(struct nb_cb_modify_args *args)
{
return NB_OK;
}
int dummy_destroy(struct nb_cb_destroy_args *args)
{
return NB_OK;
}

130
pathd/path_nb.h Normal file
View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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_PATH_NB_H_
#define _FRR_PATH_NB_H_
#include "pathd/pathd.h"
extern const struct frr_yang_module_info frr_pathd_info;
/* Mandatory callbacks. */
int pathd_srte_segment_list_create(struct nb_cb_create_args *args);
int pathd_srte_segment_list_destroy(struct nb_cb_destroy_args *args);
const void *pathd_srte_segment_list_get_next(struct nb_cb_get_next_args *args);
int pathd_srte_segment_list_get_keys(struct nb_cb_get_keys_args *args);
const void *
pathd_srte_segment_list_lookup_entry(struct nb_cb_lookup_entry_args *args);
int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args);
int pathd_srte_segment_list_segment_destroy(struct nb_cb_destroy_args *args);
int pathd_srte_segment_list_protocol_origin_modify(
struct nb_cb_modify_args *args);
int pathd_srte_segment_list_originator_modify(struct nb_cb_modify_args *args);
int pathd_srte_segment_list_segment_sid_value_modify(
struct nb_cb_modify_args *args);
int pathd_srte_segment_list_segment_nai_destroy(
struct nb_cb_destroy_args *args);
void pathd_srte_segment_list_segment_nai_apply_finish(
struct nb_cb_apply_finish_args *args);
int pathd_srte_policy_create(struct nb_cb_create_args *args);
int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args);
const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args);
int pathd_srte_policy_get_keys(struct nb_cb_get_keys_args *args);
const void *
pathd_srte_policy_lookup_entry(struct nb_cb_lookup_entry_args *args);
int pathd_srte_policy_name_modify(struct nb_cb_modify_args *args);
int pathd_srte_policy_name_destroy(struct nb_cb_destroy_args *args);
int pathd_srte_policy_binding_sid_modify(struct nb_cb_modify_args *args);
int pathd_srte_policy_binding_sid_destroy(struct nb_cb_destroy_args *args);
struct yang_data *
pathd_srte_policy_is_operational_get_elem(struct nb_cb_get_elem_args *args);
int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args);
int pathd_srte_policy_candidate_path_destroy(struct nb_cb_destroy_args *args);
int pathd_srte_policy_candidate_path_name_modify(
struct nb_cb_modify_args *args);
const void *
pathd_srte_policy_candidate_path_get_next(struct nb_cb_get_next_args *args);
int pathd_srte_policy_candidate_path_get_keys(struct nb_cb_get_keys_args *args);
const void *pathd_srte_policy_candidate_path_lookup_entry(
struct nb_cb_lookup_entry_args *args);
void pathd_srte_policy_candidate_path_bandwidth_apply_finish(
struct nb_cb_apply_finish_args *args);
int pathd_srte_policy_candidate_path_bandwidth_destroy(
struct nb_cb_destroy_args *args);
int pathd_srte_policy_candidate_path_exclude_any_modify(
struct nb_cb_modify_args *args);
int pathd_srte_policy_candidate_path_exclude_any_destroy(
struct nb_cb_destroy_args *args);
int pathd_srte_policy_candidate_path_include_any_modify(
struct nb_cb_modify_args *args);
int pathd_srte_policy_candidate_path_include_any_destroy(
struct nb_cb_destroy_args *args);
int pathd_srte_policy_candidate_path_include_all_modify(
struct nb_cb_modify_args *args);
int pathd_srte_policy_candidate_path_include_all_destroy(
struct nb_cb_destroy_args *args);
int pathd_srte_policy_candidate_path_metrics_destroy(
struct nb_cb_destroy_args *args);
void pathd_srte_policy_candidate_path_metrics_apply_finish(
struct nb_cb_apply_finish_args *args);
int pathd_srte_policy_candidate_path_objfun_destroy(
struct nb_cb_destroy_args *args);
void pathd_srte_policy_candidate_path_objfun_apply_finish(
struct nb_cb_apply_finish_args *args);
struct yang_data *
pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem(
struct nb_cb_get_elem_args *args);
int pathd_srte_policy_candidate_path_protocol_origin_modify(
struct nb_cb_modify_args *args);
int pathd_srte_policy_candidate_path_originator_modify(
struct nb_cb_modify_args *args);
struct yang_data *pathd_srte_policy_candidate_path_discriminator_get_elem(
struct nb_cb_get_elem_args *args);
int pathd_srte_policy_candidate_path_type_modify(
struct nb_cb_modify_args *args);
int pathd_srte_policy_candidate_path_segment_list_name_modify(
struct nb_cb_modify_args *args);
int pathd_srte_policy_candidate_path_segment_list_name_destroy(
struct nb_cb_destroy_args *args);
/* Optional 'apply_finish' callbacks. */
void pathd_apply_finish(struct nb_cb_apply_finish_args *args);
/* Optional 'cli_show' callbacks. */
void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_srte_segment_list_segment(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_srte_policy(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_srte_policy_name(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_srte_policy_binding_sid(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_srte_policy_candidate_path(struct vty *vty,
struct lyd_node *dnode,
bool show_defaults);
/* Utility functions */
typedef void (*of_pref_cp_t)(enum objfun_type type, void *arg);
void iter_objfun_prefs(const struct lyd_node *dnode, const char *path,
of_pref_cp_t fun, void *arg);
#endif /* _FRR_PATH_NB_H_ */

731
pathd/path_nb_config.c Normal file
View File

@ -0,0 +1,731 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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 <lib_errors.h>
#include "northbound.h"
#include "libfrr.h"
#include "pathd/path_zebra.h"
#include "pathd/path_nb.h"
/*
* XPath: /frr-pathd:pathd
*/
void pathd_apply_finish(struct nb_cb_apply_finish_args *args)
{
srte_apply_changes();
}
/*
* XPath: /frr-pathd:pathd/srte/segment-list
*/
int pathd_srte_segment_list_create(struct nb_cb_create_args *args)
{
struct srte_segment_list *segment_list;
const char *name;
if (args->event != NB_EV_APPLY)
return NB_OK;
name = yang_dnode_get_string(args->dnode, "./name");
segment_list = srte_segment_list_add(name);
nb_running_set_entry(args->dnode, segment_list);
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW);
return NB_OK;
}
int pathd_srte_segment_list_destroy(struct nb_cb_destroy_args *args)
{
struct srte_segment_list *segment_list;
if (args->event != NB_EV_APPLY)
return NB_OK;
segment_list = nb_running_unset_entry(args->dnode);
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_DELETED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/segment-list/protocol-origin
*/
int pathd_srte_segment_list_protocol_origin_modify(
struct nb_cb_modify_args *args)
{
struct srte_segment_list *segment_list;
if (args->event != NB_EV_APPLY)
return NB_OK;
segment_list = nb_running_get_entry(args->dnode, NULL, true);
segment_list->protocol_origin = yang_dnode_get_enum(args->dnode, NULL);
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/segment-list/originator
*/
int pathd_srte_segment_list_originator_modify(struct nb_cb_modify_args *args)
{
struct srte_segment_list *segment_list;
const char *originator;
if (args->event != NB_EV_APPLY)
return NB_OK;
segment_list = nb_running_get_entry(args->dnode, NULL, true);
originator = yang_dnode_get_string(args->dnode, NULL);
strlcpy(segment_list->originator, originator,
sizeof(segment_list->originator));
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/segment-list/segment
*/
int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args)
{
struct srte_segment_list *segment_list;
struct srte_segment_entry *segment;
uint32_t index;
if (args->event != NB_EV_APPLY)
return NB_OK;
segment_list = nb_running_get_entry(args->dnode, NULL, true);
index = yang_dnode_get_uint32(args->dnode, "./index");
segment = srte_segment_entry_add(segment_list, index);
nb_running_set_entry(args->dnode, segment);
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
return NB_OK;
}
int pathd_srte_segment_list_segment_destroy(struct nb_cb_destroy_args *args)
{
struct srte_segment_entry *segment;
if (args->event != NB_EV_APPLY)
return NB_OK;
segment = nb_running_unset_entry(args->dnode);
SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED);
srte_segment_entry_del(segment);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/segment-list/segment/sid-value
*/
int pathd_srte_segment_list_segment_sid_value_modify(
struct nb_cb_modify_args *args)
{
mpls_label_t sid_value;
struct srte_segment_entry *segment;
if (args->event != NB_EV_APPLY)
return NB_OK;
segment = nb_running_get_entry(args->dnode, NULL, true);
sid_value = yang_dnode_get_uint32(args->dnode, NULL);
segment->sid_value = sid_value;
SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED);
return NB_OK;
}
int pathd_srte_segment_list_segment_nai_destroy(struct nb_cb_destroy_args *args)
{
struct srte_segment_entry *segment;
if (args->event != NB_EV_APPLY)
return NB_OK;
segment = nb_running_get_entry(args->dnode, NULL, true);
segment->nai_type = SRTE_SEGMENT_NAI_TYPE_NONE;
segment->nai_local_addr.ipa_type = IPADDR_NONE;
segment->nai_local_iface = 0;
segment->nai_remote_addr.ipa_type = IPADDR_NONE;
segment->nai_remote_iface = 0;
return NB_OK;
}
void pathd_srte_segment_list_segment_nai_apply_finish(
struct nb_cb_apply_finish_args *args)
{
struct srte_segment_entry *segment;
enum srte_segment_nai_type type;
struct ipaddr local_addr, remote_addr;
uint32_t local_iface = 0, remote_iface = 0;
segment = nb_running_get_entry(args->dnode, NULL, true);
type = yang_dnode_get_enum(args->dnode, "./type");
yang_dnode_get_ip(&local_addr, args->dnode, "./local-address");
switch (type) {
case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
break;
case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
yang_dnode_get_ip(&remote_addr, args->dnode,
"./remote-address");
break;
case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
yang_dnode_get_ip(&remote_addr, args->dnode,
"./remote-address");
local_iface =
yang_dnode_get_uint32(args->dnode, "./local-interface");
remote_iface = yang_dnode_get_uint32(args->dnode,
"./remote-interface");
break;
default:
break;
}
srte_segment_entry_set_nai(segment, type, &local_addr, local_iface,
&remote_addr, remote_iface);
}
/*
* XPath: /frr-pathd:pathd/srte/policy
*/
int pathd_srte_policy_create(struct nb_cb_create_args *args)
{
struct srte_policy *policy;
uint32_t color;
struct ipaddr endpoint;
if (args->event != NB_EV_APPLY)
return NB_OK;
color = yang_dnode_get_uint32(args->dnode, "./color");
yang_dnode_get_ip(&endpoint, args->dnode, "./endpoint");
policy = srte_policy_add(color, &endpoint);
nb_running_set_entry(args->dnode, policy);
SET_FLAG(policy->flags, F_POLICY_NEW);
return NB_OK;
}
int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args)
{
struct srte_policy *policy;
if (args->event != NB_EV_APPLY)
return NB_OK;
policy = nb_running_unset_entry(args->dnode);
SET_FLAG(policy->flags, F_POLICY_DELETED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/policy/name
*/
int pathd_srte_policy_name_modify(struct nb_cb_modify_args *args)
{
struct srte_policy *policy;
const char *name;
if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE)
return NB_OK;
policy = nb_running_get_entry(args->dnode, NULL, true);
if (args->event == NB_EV_VALIDATE) {
/* the policy name is fixed after setting it once */
if (strlen(policy->name) > 0) {
flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
"The SR Policy name is fixed!");
return NB_ERR_RESOURCE;
} else
return NB_OK;
}
name = yang_dnode_get_string(args->dnode, NULL);
strlcpy(policy->name, name, sizeof(policy->name));
SET_FLAG(policy->flags, F_POLICY_MODIFIED);
return NB_OK;
}
int pathd_srte_policy_name_destroy(struct nb_cb_destroy_args *args)
{
struct srte_policy *policy;
if (args->event != NB_EV_APPLY)
return NB_OK;
policy = nb_running_get_entry(args->dnode, NULL, true);
policy->name[0] = '\0';
SET_FLAG(policy->flags, F_POLICY_MODIFIED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/policy/binding-sid
*/
int pathd_srte_policy_binding_sid_modify(struct nb_cb_modify_args *args)
{
struct srte_policy *policy;
mpls_label_t binding_sid;
policy = nb_running_get_entry(args->dnode, NULL, true);
binding_sid = yang_dnode_get_uint32(args->dnode, NULL);
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
if (path_zebra_request_label(binding_sid) < 0)
return NB_ERR_RESOURCE;
break;
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
srte_policy_update_binding_sid(policy, binding_sid);
SET_FLAG(policy->flags, F_POLICY_MODIFIED);
break;
}
return NB_OK;
}
int pathd_srte_policy_binding_sid_destroy(struct nb_cb_destroy_args *args)
{
struct srte_policy *policy;
if (args->event != NB_EV_APPLY)
return NB_OK;
policy = nb_running_get_entry(args->dnode, NULL, true);
srte_policy_update_binding_sid(policy, MPLS_LABEL_NONE);
SET_FLAG(policy->flags, F_POLICY_MODIFIED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path
*/
int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args)
{
struct srte_policy *policy;
struct srte_candidate *candidate;
uint32_t preference;
if (args->event != NB_EV_APPLY)
return NB_OK;
policy = nb_running_get_entry(args->dnode, NULL, true);
preference = yang_dnode_get_uint32(args->dnode, "./preference");
candidate = srte_candidate_add(policy, preference);
nb_running_set_entry(args->dnode, candidate);
SET_FLAG(candidate->flags, F_CANDIDATE_NEW);
return NB_OK;
}
int pathd_srte_policy_candidate_path_destroy(struct nb_cb_destroy_args *args)
{
struct srte_candidate *candidate;
if (args->event != NB_EV_APPLY)
return NB_OK;
candidate = nb_running_unset_entry(args->dnode);
SET_FLAG(candidate->flags, F_CANDIDATE_DELETED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path/name
*/
int pathd_srte_policy_candidate_path_name_modify(struct nb_cb_modify_args *args)
{
struct srte_candidate *candidate;
const char *name;
char xpath[XPATH_MAXLEN];
char xpath_buf[XPATH_MAXLEN - 3];
if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE)
return NB_OK;
/* the candidate name is fixed after setting it once, this is checked
* here */
if (args->event == NB_EV_VALIDATE) {
/* first get the precise path to the candidate path */
yang_dnode_get_path(args->dnode, xpath_buf, sizeof(xpath_buf));
snprintf(xpath, sizeof(xpath), "%s%s", xpath_buf, "/..");
candidate = nb_running_get_entry_non_rec(NULL, xpath, false);
/* then check if it exists and if the name was provided */
if (candidate && strlen(candidate->name) > 0) {
flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
"The candidate name is fixed!");
return NB_ERR_RESOURCE;
} else
return NB_OK;
}
candidate = nb_running_get_entry(args->dnode, NULL, true);
name = yang_dnode_get_string(args->dnode, NULL);
strlcpy(candidate->name, name, sizeof(candidate->name));
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
return NB_OK;
}
static int affinity_filter_modify(struct nb_cb_modify_args *args,
enum affinity_filter_type type)
{
uint32_t filter;
struct srte_candidate *candidate;
if (args->event != NB_EV_APPLY)
return NB_OK;
assert(args->context != NULL);
candidate = nb_running_get_entry(args->dnode, NULL, true);
filter = yang_dnode_get_uint32(args->dnode, NULL);
srte_candidate_set_affinity_filter(candidate, type, filter);
return NB_OK;
}
static int affinity_filter_destroy(struct nb_cb_destroy_args *args,
enum affinity_filter_type type)
{
struct srte_candidate *candidate;
if (args->event != NB_EV_APPLY)
return NB_OK;
assert(args->context != NULL);
candidate = nb_running_get_entry(args->dnode, NULL, true);
srte_candidate_unset_affinity_filter(candidate, type);
return NB_OK;
}
/*
* XPath:
* /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/exclude-any
*/
int pathd_srte_policy_candidate_path_exclude_any_modify(
struct nb_cb_modify_args *args)
{
return affinity_filter_modify(args, AFFINITY_FILTER_EXCLUDE_ANY);
}
int pathd_srte_policy_candidate_path_exclude_any_destroy(
struct nb_cb_destroy_args *args)
{
return affinity_filter_destroy(args, AFFINITY_FILTER_EXCLUDE_ANY);
}
/*
* XPath:
* /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-any
*/
int pathd_srte_policy_candidate_path_include_any_modify(
struct nb_cb_modify_args *args)
{
return affinity_filter_modify(args, AFFINITY_FILTER_INCLUDE_ANY);
}
int pathd_srte_policy_candidate_path_include_any_destroy(
struct nb_cb_destroy_args *args)
{
return affinity_filter_destroy(args, AFFINITY_FILTER_INCLUDE_ANY);
}
/*
* XPath:
* /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-all
*/
int pathd_srte_policy_candidate_path_include_all_modify(
struct nb_cb_modify_args *args)
{
return affinity_filter_modify(args, AFFINITY_FILTER_INCLUDE_ALL);
}
int pathd_srte_policy_candidate_path_include_all_destroy(
struct nb_cb_destroy_args *args)
{
return affinity_filter_destroy(args, AFFINITY_FILTER_INCLUDE_ALL);
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics
*/
int pathd_srte_policy_candidate_path_metrics_destroy(
struct nb_cb_destroy_args *args)
{
struct srte_candidate *candidate;
enum srte_candidate_metric_type type;
if (args->event != NB_EV_APPLY)
return NB_OK;
assert(args->context != NULL);
candidate = nb_running_get_entry(args->dnode, NULL, true);
type = yang_dnode_get_enum(args->dnode, "./type");
srte_candidate_unset_metric(candidate, type);
return NB_OK;
}
void pathd_srte_policy_candidate_path_metrics_apply_finish(
struct nb_cb_apply_finish_args *args)
{
struct srte_candidate *candidate;
enum srte_candidate_metric_type type;
float value;
bool required, is_bound = false, is_computed = false;
assert(args->context != NULL);
candidate = nb_running_get_entry(args->dnode, NULL, true);
type = yang_dnode_get_enum(args->dnode, "./type");
value = (float)yang_dnode_get_dec64(args->dnode, "./value");
required = yang_dnode_get_bool(args->dnode, "./required");
if (yang_dnode_exists(args->dnode, "./is-bound"))
is_bound = yang_dnode_get_bool(args->dnode, "./is-bound");
if (yang_dnode_exists(args->dnode, "./is-computed"))
is_computed = yang_dnode_get_bool(args->dnode, "./is-computed");
srte_candidate_set_metric(candidate, type, value, required, is_bound,
is_computed);
}
/*
* XPath:
* /frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function
*/
int pathd_srte_policy_candidate_path_objfun_destroy(
struct nb_cb_destroy_args *args)
{
struct srte_candidate *candidate;
if (args->event != NB_EV_APPLY)
return NB_OK;
assert(args->context != NULL);
candidate = nb_running_get_entry(args->dnode, NULL, true);
srte_candidate_unset_objfun(candidate);
return NB_OK;
}
void pathd_srte_policy_candidate_path_objfun_apply_finish(
struct nb_cb_apply_finish_args *args)
{
struct srte_candidate *candidate;
enum objfun_type type;
bool required;
candidate = nb_running_get_entry(args->dnode, NULL, true);
required = yang_dnode_get_bool(args->dnode, "./required");
type = yang_dnode_get_enum(args->dnode, "./type");
srte_candidate_set_objfun(candidate, required, type);
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path/protocol-origin
*/
int pathd_srte_policy_candidate_path_protocol_origin_modify(
struct nb_cb_modify_args *args)
{
struct srte_candidate *candidate;
enum srte_protocol_origin protocol_origin;
if (args->event != NB_EV_APPLY)
return NB_OK;
candidate = nb_running_get_entry(args->dnode, NULL, true);
protocol_origin = yang_dnode_get_enum(args->dnode, NULL);
candidate->protocol_origin = protocol_origin;
candidate->lsp->protocol_origin = protocol_origin;
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path/originator
*/
int pathd_srte_policy_candidate_path_originator_modify(
struct nb_cb_modify_args *args)
{
struct srte_candidate *candidate;
const char *originator;
if (args->event != NB_EV_APPLY)
return NB_OK;
candidate = nb_running_get_entry(args->dnode, NULL, true);
originator = yang_dnode_get_string(args->dnode, NULL);
strlcpy(candidate->originator, originator,
sizeof(candidate->originator));
strlcpy(candidate->lsp->originator, originator,
sizeof(candidate->lsp->originator));
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path/type
*/
int pathd_srte_policy_candidate_path_type_modify(struct nb_cb_modify_args *args)
{
struct srte_candidate *candidate;
enum srte_candidate_type type;
char xpath[XPATH_MAXLEN];
char xpath_buf[XPATH_MAXLEN - 3];
if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE)
return NB_OK;
/* the candidate type is fixed after setting it once, this is checked
* here */
if (args->event == NB_EV_VALIDATE) {
/* first get the precise path to the candidate path */
yang_dnode_get_path(args->dnode, xpath_buf, sizeof(xpath_buf));
snprintf(xpath, sizeof(xpath), "%s%s", xpath_buf, "/..");
candidate = nb_running_get_entry_non_rec(NULL, xpath, false);
/* then check if it exists and if the type was provided */
if (candidate
&& candidate->type != SRTE_CANDIDATE_TYPE_UNDEFINED) {
flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
"The candidate type is fixed!");
return NB_ERR_RESOURCE;
} else
return NB_OK;
}
candidate = nb_running_get_entry(args->dnode, NULL, true);
type = yang_dnode_get_enum(args->dnode, NULL);
candidate->type = type;
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path/segment-list-name
*/
int pathd_srte_policy_candidate_path_segment_list_name_modify(
struct nb_cb_modify_args *args)
{
struct srte_candidate *candidate;
const char *segment_list_name;
candidate = nb_running_get_entry(args->dnode, NULL, true);
segment_list_name = yang_dnode_get_string(args->dnode, NULL);
if (args->event != NB_EV_APPLY)
return NB_OK;
candidate->segment_list = srte_segment_list_find(segment_list_name);
candidate->lsp->segment_list = candidate->segment_list;
assert(candidate->segment_list);
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
return NB_OK;
}
int pathd_srte_policy_candidate_path_segment_list_name_destroy(
struct nb_cb_destroy_args *args)
{
struct srte_candidate *candidate;
if (args->event != NB_EV_APPLY)
return NB_OK;
candidate = nb_running_get_entry(args->dnode, NULL, true);
candidate->segment_list = NULL;
candidate->lsp->segment_list = NULL;
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
return NB_OK;
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth
*/
void pathd_srte_policy_candidate_path_bandwidth_apply_finish(
struct nb_cb_apply_finish_args *args)
{
struct srte_candidate *candidate;
float value;
bool required;
assert(args->context != NULL);
candidate = nb_running_get_entry(args->dnode, NULL, true);
value = (float)yang_dnode_get_dec64(args->dnode, "./value");
required = yang_dnode_get_bool(args->dnode, "./required");
srte_candidate_set_bandwidth(candidate, value, required);
}
int pathd_srte_policy_candidate_path_bandwidth_destroy(
struct nb_cb_destroy_args *args)
{
struct srte_candidate *candidate;
if (args->event != NB_EV_APPLY)
return NB_OK;
assert(args->context != NULL);
candidate = nb_running_get_entry(args->dnode, NULL, true);
srte_candidate_unset_bandwidth(candidate);
return NB_OK;
}

189
pathd/path_nb_state.c Normal file
View File

@ -0,0 +1,189 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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 "log.h"
#include "prefix.h"
#include "table.h"
#include "command.h"
#include "northbound.h"
#include "libfrr.h"
#include "pathd/pathd.h"
#include "pathd/path_nb.h"
/*
* XPath: /frr-pathd:pathd/srte/segment-list
*/
const void *pathd_srte_segment_list_get_next(struct nb_cb_get_next_args *args)
{
struct srte_segment_list *segment_list =
(struct srte_segment_list *)args->list_entry;
if (args->list_entry == NULL)
segment_list =
RB_MIN(srte_segment_list_head, &srte_segment_lists);
else
segment_list = RB_NEXT(srte_segment_list_head, segment_list);
return segment_list;
}
int pathd_srte_segment_list_get_keys(struct nb_cb_get_keys_args *args)
{
const struct srte_segment_list *segment_list =
(struct srte_segment_list *)args->list_entry;
args->keys->num = 1;
snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%s",
segment_list->name);
return NB_OK;
}
const void *
pathd_srte_segment_list_lookup_entry(struct nb_cb_lookup_entry_args *args)
{
return srte_segment_list_find(args->keys->key[0]);
}
/*
* XPath: /frr-pathd:pathd/srte/policy
*/
const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args)
{
struct srte_policy *policy = (struct srte_policy *)args->list_entry;
if (args->list_entry == NULL)
policy = RB_MIN(srte_policy_head, &srte_policies);
else
policy = RB_NEXT(srte_policy_head, policy);
return policy;
}
int pathd_srte_policy_get_keys(struct nb_cb_get_keys_args *args)
{
const struct srte_policy *policy =
(struct srte_policy *)args->list_entry;
args->keys->num = 2;
snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%u",
policy->color);
ipaddr2str(&policy->endpoint, args->keys->key[1],
sizeof(args->keys->key[1]));
return NB_OK;
}
const void *pathd_srte_policy_lookup_entry(struct nb_cb_lookup_entry_args *args)
{
uint32_t color;
struct ipaddr endpoint;
color = yang_str2uint32(args->keys->key[0]);
yang_str2ip(args->keys->key[1], &endpoint);
return srte_policy_find(color, &endpoint);
}
/*
* XPath: /frr-pathd:pathd/srte/policy/is-operational
*/
struct yang_data *
pathd_srte_policy_is_operational_get_elem(struct nb_cb_get_elem_args *args)
{
struct srte_policy *policy = (struct srte_policy *)args->list_entry;
bool is_operational = false;
if (policy->status == SRTE_POLICY_STATUS_UP)
is_operational = true;
return yang_data_new_bool(args->xpath, is_operational);
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path
*/
const void *
pathd_srte_policy_candidate_path_get_next(struct nb_cb_get_next_args *args)
{
struct srte_policy *policy =
(struct srte_policy *)args->parent_list_entry;
struct srte_candidate *candidate =
(struct srte_candidate *)args->list_entry;
if (args->list_entry == NULL)
candidate =
RB_MIN(srte_candidate_head, &policy->candidate_paths);
else
candidate = RB_NEXT(srte_candidate_head, candidate);
return candidate;
}
int pathd_srte_policy_candidate_path_get_keys(struct nb_cb_get_keys_args *args)
{
const struct srte_candidate *candidate =
(struct srte_candidate *)args->list_entry;
args->keys->num = 1;
snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%u",
candidate->preference);
return NB_OK;
}
const void *pathd_srte_policy_candidate_path_lookup_entry(
struct nb_cb_lookup_entry_args *args)
{
struct srte_policy *policy =
(struct srte_policy *)args->parent_list_entry;
uint32_t preference;
preference = yang_str2uint32(args->keys->key[0]);
return srte_candidate_find(policy, preference);
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate_path/is-best-candidate-path
*/
struct yang_data *
pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem(
struct nb_cb_get_elem_args *args)
{
struct srte_candidate *candidate =
(struct srte_candidate *)args->list_entry;
return yang_data_new_bool(
args->xpath, CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST));
}
/*
* XPath: /frr-pathd:pathd/srte/policy/candidate-path/discriminator
*/
struct yang_data *pathd_srte_policy_candidate_path_discriminator_get_elem(
struct nb_cb_get_elem_args *args)
{
struct srte_candidate *candidate =
(struct srte_candidate *)args->list_entry;
return yang_data_new_uint32(args->xpath, candidate->discriminator);
}

339
pathd/path_pcep.c Normal file
View File

@ -0,0 +1,339 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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 <pcep_utils_counters.h>
#include "log.h"
#include "command.h"
#include "libfrr.h"
#include "printfrr.h"
#include "version.h"
#include "northbound.h"
#include "frr_pthread.h"
#include "jhash.h"
#include "termtable.h"
#include "pathd/pathd.h"
#include "pathd/path_errors.h"
#include "pathd/path_pcep_memory.h"
#include "pathd/path_pcep.h"
#include "pathd/path_pcep_cli.h"
#include "pathd/path_pcep_controller.h"
#include "pathd/path_pcep_lib.h"
#include "pathd/path_pcep_config.h"
/*
* Globals.
*/
static struct pcep_glob pcep_glob_space = {.dbg = {0, "pathd module: pcep"}};
struct pcep_glob *pcep_g = &pcep_glob_space;
/* Main Thread Even Handler */
static int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id,
void *payload);
static int pcep_main_event_start_sync(int pcc_id);
static int pcep_main_event_start_sync_cb(struct path *path, void *arg);
static int pcep_main_event_update_candidate(struct path *path);
static int pcep_main_event_remove_candidate_segments(const char *originator,
bool force);
/* Hook Handlers called from the Main Thread */
static int pathd_candidate_created_handler(struct srte_candidate *candidate);
static int pathd_candidate_updated_handler(struct srte_candidate *candidate);
static int pathd_candidate_removed_handler(struct srte_candidate *candidate);
/* Path manipulation functions */
static struct path_metric *pcep_copy_metrics(struct path_metric *metric);
static struct path_hop *pcep_copy_hops(struct path_hop *hop);
/* Module Functions */
static int pcep_module_finish(void);
static int pcep_module_late_init(struct thread_master *tm);
static int pcep_module_init(void);
/* ------------ Path Helper Functions ------------ */
struct path *pcep_new_path(void)
{
struct path *path;
path = XCALLOC(MTYPE_PCEP, sizeof(*path));
path->binding_sid = MPLS_LABEL_NONE;
path->enforce_bandwidth = true;
return path;
}
struct path_hop *pcep_new_hop(void)
{
struct path_hop *hop;
hop = XCALLOC(MTYPE_PCEP, sizeof(*hop));
return hop;
}
struct path_metric *pcep_new_metric(void)
{
struct path_metric *metric;
metric = XCALLOC(MTYPE_PCEP, sizeof(*metric));
return metric;
}
struct path_metric *pcep_copy_metrics(struct path_metric *metric)
{
if (metric == NULL)
return NULL;
struct path_metric *new_metric = pcep_new_metric();
*new_metric = *metric;
new_metric->next = pcep_copy_metrics(metric->next);
return new_metric;
}
struct path_hop *pcep_copy_hops(struct path_hop *hop)
{
if (hop == NULL)
return NULL;
struct path_hop *new_hop = pcep_new_hop();
*new_hop = *hop;
new_hop->next = pcep_copy_hops(hop->next);
return new_hop;
}
struct path *pcep_copy_path(struct path *path)
{
struct path *new_path = pcep_new_path();
*new_path = *path;
new_path->first_metric = pcep_copy_metrics(path->first_metric);
new_path->first_hop = pcep_copy_hops(path->first_hop);
if (path->name != NULL)
new_path->name = XSTRDUP(MTYPE_PCEP, path->name);
if (path->originator != NULL)
new_path->originator = XSTRDUP(MTYPE_PCEP, path->originator);
return new_path;
}
void pcep_free_path(struct path *path)
{
struct path_hop *hop;
struct path_metric *metric;
char *tmp;
metric = path->first_metric;
while (metric != NULL) {
struct path_metric *next = metric->next;
XFREE(MTYPE_PCEP, metric);
metric = next;
}
hop = path->first_hop;
while (hop != NULL) {
struct path_hop *next = hop->next;
XFREE(MTYPE_PCEP, hop);
hop = next;
}
if (path->originator != NULL) {
/* The path own the memory, it is const so it is clear it
shouldn't be modified. XFREE macro do not support type casting
so we need a temporary variable */
tmp = (char *)path->originator;
XFREE(MTYPE_PCEP, tmp);
path->originator = NULL;
}
if (path->name != NULL) {
/* The path own the memory, it is const so it is clear it
shouldn't be modified. XFREE macro do not support type casting
so we need a temporary variable */
tmp = (char *)path->name;
XFREE(MTYPE_PCEP, tmp);
path->name = NULL;
}
XFREE(MTYPE_PCEP, path);
}
/* ------------ Main Thread Even Handler ------------ */
int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id,
void *payload)
{
int ret = 0;
switch (type) {
case PCEP_MAIN_EVENT_START_SYNC:
ret = pcep_main_event_start_sync(pcc_id);
break;
case PCEP_MAIN_EVENT_UPDATE_CANDIDATE:
assert(payload != NULL);
ret = pcep_main_event_update_candidate((struct path *)payload);
break;
case PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP:
ret = pcep_main_event_remove_candidate_segments(
(const char *)payload, true);
break;
default:
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Unexpected event received in the main thread: %u",
type);
break;
}
return ret;
}
int pcep_main_event_start_sync(int pcc_id)
{
path_pcep_config_list_path(pcep_main_event_start_sync_cb, &pcc_id);
pcep_ctrl_sync_done(pcep_g->fpt, pcc_id);
return 0;
}
int pcep_main_event_start_sync_cb(struct path *path, void *arg)
{
int *pcc_id = (int *)arg;
pcep_ctrl_sync_path(pcep_g->fpt, *pcc_id, path);
return 1;
}
int pcep_main_event_update_candidate(struct path *path)
{
struct path *resp = NULL;
int ret = 0;
ret = path_pcep_config_update_path(path);
if (ret != PATH_NB_ERR && path->srp_id != 0) {
/* ODL and Cisco requires the first reported
* LSP to have a DOWN status, the later status changes
* will be comunicated through hook calls.
*/
enum pcep_lsp_operational_status real_status;
if ((resp = path_pcep_config_get_path(&path->nbkey))) {
resp->srp_id = path->srp_id;
real_status = resp->status;
resp->status = PCEP_LSP_OPERATIONAL_DOWN;
pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp);
/* If the update did not have any effect and the real
* status is not DOWN, we need to send a second report
* so the PCE is aware of the real status. This is due
* to the fact that NO notification will be received
* if the update did not apply any changes */
if ((ret == PATH_NB_NO_CHANGE)
&& (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
resp->status = real_status;
resp->srp_id = 0;
pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id,
resp);
}
pcep_free_path(resp);
}
}
return ret;
}
int pcep_main_event_remove_candidate_segments(const char *originator,
bool force)
{
srte_candidate_unset_segment_list(originator, force);
/* Avoid compiler warnings about const char* */
void *free_ptr = (void *)originator;
XFREE(MTYPE_PCEP, free_ptr);
srte_apply_changes();
return 0;
}
/* ------------ Hook Handlers Functions Called From Main Thread ------------ */
int pathd_candidate_created_handler(struct srte_candidate *candidate)
{
struct path *path = candidate_to_path(candidate);
int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_CREATED, path);
return ret;
}
int pathd_candidate_updated_handler(struct srte_candidate *candidate)
{
struct path *path = candidate_to_path(candidate);
int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_UPDATED, path);
return ret;
}
int pathd_candidate_removed_handler(struct srte_candidate *candidate)
{
struct path *path = candidate_to_path(candidate);
int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_REMOVED, path);
return ret;
}
/* ------------ Module Functions ------------ */
int pcep_module_late_init(struct thread_master *tm)
{
assert(pcep_g->fpt == NULL);
assert(pcep_g->master == NULL);
struct frr_pthread *fpt;
if (pcep_ctrl_initialize(tm, &fpt, pcep_main_event_handler))
return 1;
if (pcep_lib_initialize(fpt))
return 1;
pcep_g->master = tm;
pcep_g->fpt = fpt;
hook_register(pathd_candidate_created, pathd_candidate_created_handler);
hook_register(pathd_candidate_updated, pathd_candidate_updated_handler);
hook_register(pathd_candidate_removed, pathd_candidate_removed_handler);
hook_register(frr_fini, pcep_module_finish);
pcep_cli_init();
return 0;
}
int pcep_module_finish(void)
{
pcep_ctrl_finalize(&pcep_g->fpt);
pcep_lib_finalize();
for (int i = 0; i < MAX_PCC; i++)
if (pcep_g->pce_opts_cli[i] != NULL)
XFREE(MTYPE_PCEP, pcep_g->pce_opts_cli[i]);
return 0;
}
int pcep_module_init(void)
{
pcep_g->num_pce_opts_cli = 0;
for (int i = 0; i < MAX_PCE; i++)
pcep_g->pce_opts_cli[i] = NULL;
pcep_g->num_config_group_opts = 0;
for (int i = 0; i < MAX_PCE; i++)
pcep_g->config_group_opts[i] = NULL;
hook_register(frr_late_init, pcep_module_late_init);
return 0;
}
FRR_MODULE_SETUP(.name = "frr_pathd_pcep", .version = FRR_VERSION,
.description = "FRR pathd PCEP module",
.init = pcep_module_init)

326
pathd/path_pcep.h Normal file
View File

@ -0,0 +1,326 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PATH_PCEP_H_
#define _PATH_PCEP_H_
#include <stdbool.h>
#include <debug.h>
#include <netinet/tcp.h>
#include <pcep_utils_logging.h>
#include <pcep_pcc_api.h>
#include "mpls.h"
#include "pathd/pathd.h"
#include "pathd/path_pcep_memory.h"
#define PCEP_DEFAULT_PORT 4189
#define MAX_PCC 32
#define MAX_PCE 32
#define MAX_TAG_SIZE 50
#define PCEP_DEBUG_MODE_BASIC 0x01
#define PCEP_DEBUG_MODE_PATH 0x02
#define PCEP_DEBUG_MODE_PCEP 0x04
#define PCEP_DEBUG_MODE_PCEPLIB 0x08
#define PCEP_DEBUG(fmt, ...) \
do { \
if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC)) \
DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \
} while (0)
#define PCEP_DEBUG_PATH(fmt, ...) \
do { \
if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH)) \
DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \
} while (0)
#define PCEP_DEBUG_PCEP(fmt, ...) \
do { \
if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP)) \
DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \
} while (0)
#define PCEP_DEBUG_PCEPLIB(priority, fmt, ...) \
do { \
switch (priority) { \
case LOG_DEBUG: \
if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \
PCEP_DEBUG_MODE_PCEPLIB)) \
DEBUGD(&pcep_g->dbg, "pcep: " fmt, \
##__VA_ARGS__); \
break; \
case LOG_INFO: \
if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \
PCEP_DEBUG_MODE_PCEPLIB)) \
DEBUGI(&pcep_g->dbg, "pcep: " fmt, \
##__VA_ARGS__); \
break; \
case LOG_NOTICE: \
if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \
PCEP_DEBUG_MODE_PCEPLIB)) \
DEBUGN(&pcep_g->dbg, "pcep: " fmt, \
##__VA_ARGS__); \
break; \
case LOG_WARNING: \
case LOG_ERR: \
default: \
zlog(priority, "pcep: " fmt, ##__VA_ARGS__); \
break; \
} \
} while (0)
struct pcep_config_group_opts {
char name[64];
char tcp_md5_auth[TCP_MD5SIG_MAXKEYLEN];
struct ipaddr source_ip;
short source_port;
bool draft07;
bool pce_initiated;
int keep_alive_seconds;
int min_keep_alive_seconds;
int max_keep_alive_seconds;
int dead_timer_seconds;
int min_dead_timer_seconds;
int max_dead_timer_seconds;
int pcep_request_time_seconds;
int session_timeout_inteval_seconds;
int delegation_timeout_seconds;
};
struct pce_opts {
struct ipaddr addr;
short port;
char pce_name[64];
struct pcep_config_group_opts config_opts;
uint8_t precedence; /* Multi-PCE precedence */
};
struct pcc_opts {
struct ipaddr addr;
short port;
short msd;
};
/* Encapsulate the pce_opts with needed CLI information */
struct pce_opts_cli {
struct pce_opts pce_opts;
char config_group_name[64];
/* These are the values configured in the pcc-peer sub-commands.
* These need to be stored for later merging. Notice, it could
* be that not all of them are set. */
struct pcep_config_group_opts pce_config_group_opts;
/* The pce_opts->config_opts will be a merge of the default values,
* optional config_group values (which overwrite default values),
* and any values configured in the pce sub-commands (which overwrite
* both default and config_group values). This flag indicates of the
* values need to be merged or not. */
bool merged;
};
struct lsp_nb_key {
uint32_t color;
struct ipaddr endpoint;
uint32_t preference;
};
struct sid_mpls {
mpls_label_t label;
uint8_t traffic_class;
bool is_bottom;
uint8_t ttl;
};
struct pcep_caps {
bool is_stateful;
/* If we know the objective functions supported by the PCE.
* If we don't know, it doesn't mean the PCE doesn't support any */
bool supported_ofs_are_known;
/* Defined if we know which objective funtions are supported by the PCE.
* One bit per objective function, the bit index being equal to
* enum pcep_objfun_type values: bit 0 is not used, bit 1 is
* PCEP_OBJFUN_MCP, up to bit 17 that is PCEP_OBJFUN_MSN */
uint32_t supported_ofs;
};
union sid {
uint32_t value;
struct sid_mpls mpls;
};
struct nai {
/* NAI type */
enum pcep_sr_subobj_nai type;
/* Local IP address*/
struct ipaddr local_addr;
/* Local interface identifier if the NAI is an unnumbered adjacency */
uint32_t local_iface;
/* Remote address if the NAI is an adjacency */
struct ipaddr remote_addr;
/* Remote interface identifier if the NAI is an unnumbered adjacency */
uint32_t remote_iface;
};
struct path_hop {
/* Pointer to the next hop in the path */
struct path_hop *next;
/* Indicateif this ia a loose or strict hop */
bool is_loose;
/* Indicate if there is an SID for the hop */
bool has_sid;
/* Indicate if the hop as a MPLS label */
bool is_mpls;
/* Indicate if the MPLS label has extra attributes (TTL, class..)*/
bool has_attribs;
/* Hop's SID if available */
union sid sid;
/* Indicate if there is a NAI for this hop */
bool has_nai;
/* NAI if available */
struct nai nai;
};
struct path_metric {
/* Pointer to the next metric */
struct path_metric *next;
/* The metric type */
enum pcep_metric_types type;
/* If the metric should be enforced */
bool enforce;
/* If the metric value is bound (a maximum) */
bool is_bound;
/* If the metric value is computed */
bool is_computed;
/* The metric value */
float value;
};
struct path {
/* Both the nbkey and the plspid are keys comming from the PCC,
but the PCE is only using the plspid. The missing key is looked up by
the PCC so we always have both */
/* The northbound key identifying this path */
struct lsp_nb_key nbkey;
/* The generated unique PLSP identifier for this path.
See draft-ietf-pce-stateful-pce */
uint32_t plsp_id;
/* The transport address the path is comming from, PCE or PCC*/
struct ipaddr sender;
/* The pcc protocol address, must be the same family as the endpoint */
struct ipaddr pcc_addr;
/* The identifier of the PCC the path is for/from. If 0 it is undefined,
meaning it hasn't be set yet or is for all the PCC */
int pcc_id;
/* The origin of the path creation */
enum srte_protocol_origin create_origin;
/* The origin of the path modification */
enum srte_protocol_origin update_origin;
/* The identifier of the entity that originated the path */
const char *originator;
/* The type of the path, for PCE initiated or updated path it is always
SRTE_CANDIDATE_TYPE_DYNAMIC */
enum srte_candidate_type type;
/* The following data comes from either the PCC or the PCE if available
*/
/* Path's binding SID */
mpls_label_t binding_sid;
/* The name of the path */
const char *name;
/* The request identifier from the PCE, when getting a path from the
PCE. See draft-ietf-pce-stateful-pce */
uint32_t srp_id;
/* The request identifier from the PCC , when getting a path from the
PCE after a computation request. See rfc5440, section-7.4 */
uint32_t req_id;
/* The operational status of the path */
enum pcep_lsp_operational_status status;
/* If true, the receiver (PCC) must remove the path.
See draft-ietf-pce-pce-initiated-lsp */
bool do_remove;
/* Indicate the given path was removed by the PCC.
See draft-ietf-pce-stateful-pce, section-7.3, flag R */
bool was_removed;
/* Indicate the path is part of the synchronization process.
See draft-ietf-pce-stateful-pce, section-7.3, flag S */
bool is_synching;
/* Indicate if the path bandwidth requirment is defined */
bool has_bandwidth;
/* Indicate if the bandwidth requirment should be enforced */
bool enforce_bandwidth;
/* Path required bandwidth if defined */
float bandwidth;
/* Specify the list of hop defining the path */
struct path_hop *first_hop;
/* Specify the list of metrics */
struct path_metric *first_metric;
/* Indicate if the path has a PCC-defined objective function */
bool has_pcc_objfun;
/* Indicate the PCC-defined objective function is required */
bool enforce_pcc_objfun;
/* PCC-defined Objective Function */
enum objfun_type pcc_objfun;
/* Indicate if the path has a PCE-defined objective function */
bool has_pce_objfun;
/* PCE-defined Objective Function */
enum objfun_type pce_objfun;
/* Indicate if some affinity filters are defined */
bool has_affinity_filters;
/* Affinity attribute filters indexed by enum affinity_filter_type - 1
*/
uint32_t affinity_filters[MAX_AFFINITY_FILTER_TYPE];
/* The following data need to be specialized for a given PCE */
/* Indicate the path is delegated to the PCE.
See draft-ietf-pce-stateful-pce, section-7.3, flag D */
bool is_delegated;
/* Indicate if the PCE wants the path to get active.
See draft-ietf-pce-stateful-pce, section-7.3, flag A */
bool go_active;
/* Indicate the given path was created by the PCE,
See draft-ietf-pce-pce-initiated-lsp, section-5.3.1, flag C */
bool was_created;
/* The following data is defined for comnputation replies */
/* Indicate that no path could be computed */
bool no_path;
};
struct pcep_glob {
struct debug dbg;
struct thread_master *master;
struct frr_pthread *fpt;
uint8_t num_pce_opts_cli;
struct pce_opts_cli *pce_opts_cli[MAX_PCE];
uint8_t num_config_group_opts;
struct pcep_config_group_opts *config_group_opts[MAX_PCE];
};
extern struct pcep_glob *pcep_g;
/* Path Helper Functions */
struct path *pcep_new_path(void);
struct path_hop *pcep_new_hop(void);
struct path_metric *pcep_new_metric(void);
struct path *pcep_copy_path(struct path *path);
void pcep_free_path(struct path *path);
#endif // _PATH_PCEP_H_

2029
pathd/path_pcep_cli.c Normal file

File diff suppressed because it is too large Load Diff

27
pathd/path_pcep_cli.h Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2020 Volta Networks, Inc
* Brady Johnson
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PATH_PCEP_CLI_H_
#define _PATH_PCEP_CLI_H_
/* PCEP CLI Functions */
void pcep_cli_init(void);
#endif // _PATH_PCEP_CLI_H_

435
pathd/path_pcep_config.c Normal file
View File

@ -0,0 +1,435 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <northbound.h>
#include <yang.h>
#include <printfrr.h>
#include <pcep-objects.h>
#include "pathd/pathd.h"
#include "pathd/path_pcep.h"
#include "pathd/path_pcep_config.h"
#include "pathd/path_pcep_debug.h"
#include "thread.h"
#define MAX_XPATH 256
#define MAX_FLOAT_LEN 22
#define INETADDR4_MAXLEN 16
#define INETADDR6_MAXLEN 40
static void copy_candidate_objfun_info(struct srte_candidate *candidate,
struct path *path);
static void copy_candidate_affinity_filters(struct srte_candidate *candidate,
struct path *path);
static struct path_hop *
path_pcep_config_list_path_hops(struct srte_segment_list *segment_list);
static struct srte_candidate *lookup_candidate(struct lsp_nb_key *key);
static char *candidate_name(struct srte_candidate *candidate);
static enum pcep_lsp_operational_status
status_int_to_ext(enum srte_policy_status status);
static enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type);
static enum srte_segment_nai_type srte_nai_type(enum pcep_sr_subobj_nai type);
static int path_pcep_config_lookup_cb(struct thread *t)
{
struct path *path = THREAD_ARG(t);
struct srte_candidate *candidate = lookup_candidate(&path->nbkey);
struct srte_lsp *lsp;
if (candidate == NULL)
return 0;
lsp = candidate->lsp;
if (path->name == NULL)
path->name = candidate_name(candidate);
if (path->type == SRTE_CANDIDATE_TYPE_UNDEFINED)
path->type = candidate->type;
if (path->create_origin == SRTE_ORIGIN_UNDEFINED)
path->create_origin = candidate->protocol_origin;
if ((path->update_origin == SRTE_ORIGIN_UNDEFINED)
&& (lsp->segment_list != NULL))
path->update_origin = lsp->segment_list->protocol_origin;
return 0;
}
void path_pcep_config_lookup(struct path *path)
{
/*
* Configuration access is strictly done via the main thread
*/
thread_execute(master, path_pcep_config_lookup_cb, path, 0);
}
struct path *path_pcep_config_get_path(struct lsp_nb_key *key)
{
struct srte_candidate *candidate = lookup_candidate(key);
if (candidate == NULL)
return NULL;
return candidate_to_path(candidate);
}
void path_pcep_config_list_path(path_list_cb_t cb, void *arg)
{
struct path *path;
struct srte_policy *policy;
struct srte_candidate *candidate;
RB_FOREACH (policy, srte_policy_head, &srte_policies) {
RB_FOREACH (candidate, srte_candidate_head,
&policy->candidate_paths) {
path = candidate_to_path(candidate);
if (!cb(path, arg))
return;
}
}
}
struct path *candidate_to_path(struct srte_candidate *candidate)
{
char *name;
struct path *path;
struct path_hop *hop = NULL;
struct path_metric *metric = NULL;
struct srte_policy *policy;
struct srte_lsp *lsp;
enum pcep_lsp_operational_status status;
enum srte_protocol_origin update_origin = 0;
char *originator = NULL;
policy = candidate->policy;
lsp = candidate->lsp;
if (lsp->segment_list != NULL) {
hop = path_pcep_config_list_path_hops(lsp->segment_list);
update_origin = lsp->segment_list->protocol_origin;
originator = XSTRDUP(MTYPE_PCEP, lsp->segment_list->originator);
}
path = pcep_new_path();
name = candidate_name(candidate);
if (CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST)) {
status = status_int_to_ext(policy->status);
} else {
status = PCEP_LSP_OPERATIONAL_DOWN;
}
for (uint32_t i = 0; i < MAX_METRIC_TYPE; i++) {
struct path_metric *path_metric;
struct srte_metric *srte_metric = &lsp->metrics[i];
if (CHECK_FLAG(srte_metric->flags, F_METRIC_IS_DEFINED)) {
path_metric = pcep_new_metric();
path_metric->next = metric;
metric = path_metric;
metric->type = i + 1;
metric->value = srte_metric->value;
metric->enforce = CHECK_FLAG(srte_metric->flags,
F_METRIC_IS_REQUIRED);
metric->is_bound = CHECK_FLAG(srte_metric->flags,
F_METRIC_IS_BOUND);
metric->is_computed = CHECK_FLAG(srte_metric->flags,
F_METRIC_IS_COMPUTED);
}
}
*path = (struct path){
.nbkey = (struct lsp_nb_key){.color = policy->color,
.endpoint = policy->endpoint,
.preference =
candidate->preference},
.create_origin = lsp->protocol_origin,
.update_origin = update_origin,
.originator = originator,
.plsp_id = 0,
.name = name,
.type = candidate->type,
.srp_id = 0,
.req_id = 0,
.binding_sid = policy->binding_sid,
.status = status,
.do_remove = false,
.go_active = false,
.was_created = false,
.was_removed = false,
.is_synching = false,
.is_delegated = false,
.first_hop = hop,
.first_metric = metric};
path->has_bandwidth = CHECK_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH);
if (path->has_bandwidth) {
path->enforce_bandwidth =
CHECK_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH);
path->bandwidth = lsp->bandwidth;
} else {
path->enforce_bandwidth = true;
path->bandwidth = 0;
}
copy_candidate_objfun_info(candidate, path);
copy_candidate_affinity_filters(candidate, path);
return path;
}
void copy_candidate_objfun_info(struct srte_candidate *candidate,
struct path *path)
{
struct srte_lsp *lsp = candidate->lsp;
if (lsp != NULL) {
if (CHECK_FLAG(lsp->flags, F_CANDIDATE_HAS_OBJFUN)) {
path->has_pce_objfun = true;
path->pce_objfun = lsp->objfun;
} else {
path->has_pce_objfun = false;
path->pce_objfun = OBJFUN_UNDEFINED;
}
}
if (CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN)) {
path->has_pcc_objfun = true;
path->pcc_objfun = candidate->objfun;
path->enforce_pcc_objfun = CHECK_FLAG(
candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN);
} else {
path->has_pcc_objfun = false;
path->pcc_objfun = OBJFUN_UNDEFINED;
UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN);
}
}
void copy_candidate_affinity_filters(struct srte_candidate *candidate,
struct path *path)
{
bool eany = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_EXCLUDE_ANY);
bool iany = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_INCLUDE_ANY);
bool iall = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_INCLUDE_ALL);
path->has_affinity_filters = eany || iany || iall;
path->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY - 1] =
eany ? candidate->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY
- 1]
: 0;
path->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY - 1] =
iany ? candidate->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY
- 1]
: 0;
path->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL - 1] =
iall ? candidate->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL
- 1]
: 0;
}
struct path_hop *
path_pcep_config_list_path_hops(struct srte_segment_list *segment_list)
{
struct srte_segment_entry *segment;
struct path_hop *hop = NULL, *last_hop = NULL;
RB_FOREACH_REVERSE (segment, srte_segment_entry_head,
&segment_list->segments) {
hop = pcep_new_hop();
*hop = (struct path_hop){
.next = last_hop,
.is_loose = false,
.has_sid = true,
.is_mpls = true,
.has_attribs = false,
.sid = {.mpls = {.label = segment->sid_value}},
.has_nai =
segment->nai_type != SRTE_SEGMENT_NAI_TYPE_NONE,
.nai = {.type = pcep_nai_type(segment->nai_type)}};
switch (segment->nai_type) {
case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
memcpy(&hop->nai.local_addr, &segment->nai_local_addr,
sizeof(struct ipaddr));
break;
case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
memcpy(&hop->nai.local_addr, &segment->nai_local_addr,
sizeof(struct ipaddr));
memcpy(&hop->nai.remote_addr, &segment->nai_remote_addr,
sizeof(struct ipaddr));
break;
case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
memcpy(&hop->nai.local_addr, &segment->nai_local_addr,
sizeof(struct ipaddr));
hop->nai.local_iface = segment->nai_local_iface;
memcpy(&hop->nai.remote_addr, &segment->nai_remote_addr,
sizeof(struct ipaddr));
hop->nai.remote_iface = segment->nai_remote_iface;
break;
default:
break;
}
last_hop = hop;
}
return hop;
}
int path_pcep_config_update_path(struct path *path)
{
assert(path != NULL);
assert(path->nbkey.preference != 0);
assert(path->nbkey.endpoint.ipa_type == IPADDR_V4);
struct path_hop *hop;
struct path_metric *metric;
int index;
char segment_list_name_buff[64 + 1 + 64 + 1 + 11 + 1];
char *segment_list_name = NULL;
struct srte_candidate *candidate;
struct srte_segment_list *segment_list = NULL;
struct srte_segment_entry *segment;
candidate = lookup_candidate(&path->nbkey);
// if there is no candidate to update we are done
if (!candidate)
return 0;
// first clean up old segment list if present
if (candidate->lsp->segment_list) {
SET_FLAG(candidate->lsp->segment_list->flags,
F_SEGMENT_LIST_DELETED);
candidate->lsp->segment_list = NULL;
}
if (path->first_hop != NULL) {
snprintf(segment_list_name_buff, sizeof(segment_list_name_buff),
"%s-%u", path->name, path->plsp_id);
segment_list_name = segment_list_name_buff;
segment_list = srte_segment_list_add(segment_list_name);
segment_list->protocol_origin = path->update_origin;
strlcpy(segment_list->originator, path->originator,
sizeof(segment_list->originator));
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW);
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
for (hop = path->first_hop, index = 10; hop != NULL;
hop = hop->next, index += 10) {
assert(hop->has_sid);
assert(hop->is_mpls);
segment = srte_segment_entry_add(segment_list, index);
segment->sid_value = (mpls_label_t)hop->sid.mpls.label;
SET_FLAG(segment->segment_list->flags,
F_SEGMENT_LIST_MODIFIED);
if (hop->has_nai)
srte_segment_entry_set_nai(
segment, srte_nai_type(hop->nai.type),
&hop->nai.local_addr,
hop->nai.local_iface,
&hop->nai.remote_addr,
hop->nai.remote_iface);
}
}
candidate->lsp->segment_list = segment_list;
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
for (metric = path->first_metric; metric != NULL; metric = metric->next)
srte_lsp_set_metric(candidate->lsp, metric->type, metric->value,
metric->enforce, metric->is_bound,
metric->is_computed);
if (path->has_bandwidth)
srte_lsp_set_bandwidth(candidate->lsp, path->bandwidth,
path->enforce_bandwidth);
if (path->has_pce_objfun) {
SET_FLAG(candidate->lsp->flags, F_CANDIDATE_HAS_OBJFUN);
candidate->lsp->objfun = path->pce_objfun;
}
srte_apply_changes();
return 0;
}
struct srte_candidate *lookup_candidate(struct lsp_nb_key *key)
{
struct srte_policy *policy = NULL;
policy = srte_policy_find(key->color, &key->endpoint);
if (policy == NULL)
return NULL;
return srte_candidate_find(policy, key->preference);
}
char *candidate_name(struct srte_candidate *candidate)
{
return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name,
candidate->name);
}
enum pcep_lsp_operational_status
status_int_to_ext(enum srte_policy_status status)
{
switch (status) {
case SRTE_POLICY_STATUS_UP:
return PCEP_LSP_OPERATIONAL_ACTIVE;
case SRTE_POLICY_STATUS_GOING_UP:
return PCEP_LSP_OPERATIONAL_GOING_UP;
case SRTE_POLICY_STATUS_GOING_DOWN:
return PCEP_LSP_OPERATIONAL_GOING_DOWN;
default:
return PCEP_LSP_OPERATIONAL_DOWN;
}
}
enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type)
{
switch (type) {
case SRTE_SEGMENT_NAI_TYPE_NONE:
return PCEP_SR_SUBOBJ_NAI_ABSENT;
case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
return PCEP_SR_SUBOBJ_NAI_IPV4_NODE;
case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
return PCEP_SR_SUBOBJ_NAI_IPV6_NODE;
case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
return PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY;
case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
return PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY;
case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
return PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY;
default:
return PCEP_SR_SUBOBJ_NAI_UNKNOWN;
}
}
enum srte_segment_nai_type srte_nai_type(enum pcep_sr_subobj_nai type)
{
switch (type) {
case PCEP_SR_SUBOBJ_NAI_ABSENT:
return SRTE_SEGMENT_NAI_TYPE_NONE;
case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
return SRTE_SEGMENT_NAI_TYPE_IPV4_NODE;
case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
return SRTE_SEGMENT_NAI_TYPE_IPV6_NODE;
case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
return SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY;
case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
return SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY;
case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
return SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY;
default:
return SRTE_SEGMENT_NAI_TYPE_NONE;
}
}

44
pathd/path_pcep_config.h Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PATH_PCEP_CONFIG_H_
#define _PATH_PCEP_CONFIG_H_
#include <stdbool.h>
#include <debug.h>
#include "pathd/path_pcep.h"
#define PATH_NB_NO_CHANGE 0
#define PATH_NB_OK 1
#define PATH_NB_ERR -1
typedef int (*path_list_cb_t)(struct path *path, void *arg);
/* Lookup the candidate path and fill up the missing path attributes like name
and type. Used for path generated from PCEP message received from the PCE
so they contains more information about the candidate path. If no matching
policy or candidate path is found, nothing is changed */
void path_pcep_config_lookup(struct path *path);
struct path *path_pcep_config_get_path(struct lsp_nb_key *key);
void path_pcep_config_list_path(path_list_cb_t cb, void *arg);
int path_pcep_config_update_path(struct path *path);
struct path *candidate_to_path(struct srte_candidate *candidate);
#endif // _PATH_PCEP_CONFIG_H_

1077
pathd/path_pcep_controller.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PATH_PCEP_CONTROLLER_H_
#define _PATH_PCEP_CONTROLLER_H_
#include "pathd/path_pcep.h"
enum pcep_main_event_type {
PCEP_MAIN_EVENT_UNDEFINED = 0,
PCEP_MAIN_EVENT_START_SYNC,
PCEP_MAIN_EVENT_UPDATE_CANDIDATE,
PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP
};
typedef int (*pcep_main_event_handler_t)(enum pcep_main_event_type type,
int pcc_id, void *payload);
enum pcep_pathd_event_type {
PCEP_PATH_UNDEFINED = 0,
PCEP_PATH_CREATED,
PCEP_PATH_UPDATED,
PCEP_PATH_REMOVED
};
struct ctrl_state {
struct thread_master *main;
struct thread_master *self;
pcep_main_event_handler_t main_event_handler;
struct pcc_opts *pcc_opts;
int pcc_count;
int pcc_last_id;
struct pcc_state *pcc[MAX_PCC];
};
/* Timer handling data structures */
enum pcep_ctrl_timeout_type { TO_UNDEFINED, TO_COMPUTATION_REQUEST, TO_MAX };
enum pcep_ctrl_timer_type {
TM_UNDEFINED,
TM_RECONNECT_PCC,
TM_PCEPLIB_TIMER,
TM_TIMEOUT,
TM_CALCULATE_BEST_PCE,
TM_SESSION_TIMEOUT_PCC,
TM_MAX
};
struct pcep_ctrl_timer_data {
struct ctrl_state *ctrl_state;
enum pcep_ctrl_timer_type timer_type;
enum pcep_ctrl_timeout_type timeout_type;
int pcc_id;
void *payload;
};
/* Socket handling data structures */
enum pcep_ctrl_socket_type { SOCK_PCEPLIB = 1 };
struct pcep_ctrl_socket_data {
struct ctrl_state *ctrl_state;
enum pcep_ctrl_socket_type type;
bool is_read;
int fd;
int pcc_id;
void *payload;
};
typedef int (*pcep_ctrl_thread_callback)(struct thread *);
/* PCC connection information, populated in a thread-safe
* manner with pcep_ctrl_get_pcc_info() */
struct pcep_pcc_info {
struct ctrl_state *ctrl_state; /* will be NULL when returned */
char pce_name[64];
int pcc_id;
struct ipaddr pcc_addr;
uint16_t pcc_port;
int status;
short msd;
uint32_t next_reqid;
uint32_t next_plspid;
bool is_best_multi_pce;
uint8_t precedence;
};
/* Functions called from the main thread */
int pcep_ctrl_initialize(struct thread_master *main_thread,
struct frr_pthread **fpt,
pcep_main_event_handler_t event_handler);
int pcep_ctrl_finalize(struct frr_pthread **fpt);
int pcep_ctrl_update_pcc_options(struct frr_pthread *fpt,
struct pcc_opts *opts);
int pcep_ctrl_update_pce_options(struct frr_pthread *fpt,
struct pce_opts *opts);
int pcep_ctrl_remove_pcc(struct frr_pthread *fpt, struct pce_opts *pce_opts);
int pcep_ctrl_reset_pcc_session(struct frr_pthread *fpt, char *pce_name);
int pcep_ctrl_pathd_event(struct frr_pthread *fpt,
enum pcep_pathd_event_type type, struct path *path);
int pcep_ctrl_sync_path(struct frr_pthread *fpt, int pcc_id, struct path *path);
int pcep_ctrl_sync_done(struct frr_pthread *fpt, int pcc_id);
struct counters_group *pcep_ctrl_get_counters(struct frr_pthread *fpt,
int pcc_id);
pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id);
struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt,
const char *pce_name);
/* Synchronously send a report, the caller is responsible to free the path,
* If `pcc_id` is `0` the report is sent by all PCCs */
void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
struct path *path);
/* Functions called from the controller thread */
void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id);
void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id,
struct path *path);
void pcep_thread_cancel_timer(struct thread **thread);
void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
int retry_count, struct thread **thread);
void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id,
enum pcep_ctrl_timeout_type type,
uint32_t delay, void *param,
struct thread **thread);
void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state,
int pcc_id, int delay,
struct thread **thread);
void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state);
void pcep_thread_schedule_sync_best_pce(struct ctrl_state *ctrl_state,
int pcc_id, int delay,
struct thread **thread);
void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state,
int delay, void *payload,
struct thread **thread,
pcep_ctrl_thread_callback cb);
int pcep_thread_socket_read(void *fpt, void **thread, int fd, void *payload,
pcep_ctrl_thread_callback cb);
int pcep_thread_socket_write(void *fpt, void **thread, int fd, void *payload,
pcep_ctrl_thread_callback cb);
int pcep_thread_send_ctrl_event(void *fpt, void *payload,
pcep_ctrl_thread_callback cb);
int pcep_thread_pcep_event(struct thread *thread);
int pcep_thread_pcc_count(struct ctrl_state *ctrl_state);
#endif // _PATH_PCEP_CONTROLLER_H_

1771
pathd/path_pcep_debug.c Normal file

File diff suppressed because it is too large Load Diff

56
pathd/path_pcep_debug.h Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PATH_PCEP_DEBUG_H_
#define _PATH_PCEP_DEBUG_H_
#include "pathd/path_debug.h"
#include <pcep_pcc_api.h>
#include <pcep-objects.h>
#include "pathd/path_pcep.h"
#include "pathd/path_pcep_controller.h"
#include "pathd/path_pcep_pcc.h"
#include "pathd/path_pcep_lib.h"
const char *pcc_status_name(enum pcc_status status);
const char *pcep_error_type_name(enum pcep_error_type error_type);
const char *pcep_error_value_name(enum pcep_error_type error_type,
enum pcep_error_value error_value);
const char *pcep_event_type_name(pcep_event_type event_type);
const char *pcep_message_type_name(enum pcep_message_types pcep_message_type);
const char *pcep_object_class_name(enum pcep_object_classes obj_class);
const char *pcep_object_type_name(enum pcep_object_classes obj_class,
enum pcep_object_types obj_type);
const char *pcep_lsp_status_name(enum pcep_lsp_operational_status status);
const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type);
const char *pcep_ro_type_name(enum pcep_ro_subobj_types ro_type);
const char *pcep_nai_type_name(enum pcep_sr_subobj_nai nai_type);
const char *pcep_metric_type_name(enum pcep_metric_types type);
const char *pcep_nopath_tlv_err_code_name(enum pcep_nopath_tlv_err_codes code);
const char *format_objfun_set(uint32_t flags);
const char *format_pcc_opts(struct pcc_opts *ops);
const char *format_pcc_state(struct pcc_state *state);
const char *format_ctrl_state(struct ctrl_state *state);
const char *format_path(struct path *path);
const char *format_pcep_event(pcep_event *event);
const char *format_pcep_message(struct pcep_message *msg);
const char *format_yang_dnode(struct lyd_node *dnode);
#endif // _PATH_PCEP_DEBUG_H_

1146
pathd/path_pcep_lib.c Normal file

File diff suppressed because it is too large Load Diff

48
pathd/path_pcep_lib.h Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PATH_PCEP_LIB_H_
#define _PATH_PCEP_LIB_H_
#include <stdbool.h>
#include <pcep_pcc_api.h>
#include "frr_pthread.h"
#include "pathd/path_pcep.h"
int pcep_lib_initialize(struct frr_pthread *fpt);
void pcep_lib_finalize(void);
pcep_session *
pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr,
int dst_port, short msd,
const struct pcep_config_group_opts *pcep_options);
void pcep_lib_disconnect(pcep_session *sess);
struct pcep_message *pcep_lib_format_report(struct pcep_caps *caps,
struct path *path);
struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps,
struct path *path);
struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid);
struct pcep_message *pcep_lib_format_error(int error_type, int error_value);
struct path *pcep_lib_parse_path(struct pcep_message *msg);
void pcep_lib_parse_capabilities(struct pcep_message *msg,
struct pcep_caps *caps);
struct counters_group *pcep_lib_copy_counters(pcep_session *sess);
void pcep_lib_free_counters(struct counters_group *counters);
pcep_session *pcep_lib_copy_pcep_session(pcep_session *sess);
#endif // _PATH_PCEP_LIB_H_

27
pathd/path_pcep_memory.c Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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 <memory.h>
#include "pathd/path_pcep_memory.h"
DEFINE_MTYPE(PATHD, PCEP, "PCEP module")
DEFINE_MTYPE(PATHD, PCEPLIB_INFRA, "PCEPlib Infrastructure")
DEFINE_MTYPE(PATHD, PCEPLIB_MESSAGES, "PCEPlib PCEP Messages")

28
pathd/path_pcep_memory.h Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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_PATH_PCEP_MEMORY_H_
#define _FRR_PATH_PCEP_MEMORY_H_
#include "pathd/path_memory.h"
DECLARE_MTYPE(PCEP)
DECLARE_MTYPE(PCEPLIB_INFRA)
DECLARE_MTYPE(PCEPLIB_MESSAGES)
#endif /* _FRR_PATH_PCEP_MEMORY_H_ */

1817
pathd/path_pcep_pcc.c Normal file

File diff suppressed because it is too large Load Diff

141
pathd/path_pcep_pcc.h Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PATH_PCEP_PCC_H_
#define _PATH_PCEP_PCC_H_
#include "typesafe.h"
#include "pathd/path_pcep.h"
enum pcc_status {
PCEP_PCC_INITIALIZED = 0,
PCEP_PCC_DISCONNECTED,
PCEP_PCC_CONNECTING,
PCEP_PCC_SYNCHRONIZING,
PCEP_PCC_OPERATING
};
PREDECL_HASH(plspid_map)
PREDECL_HASH(nbkey_map)
PREDECL_HASH(req_map)
struct plspid_map_data {
struct plspid_map_item mi;
struct lsp_nb_key nbkey;
uint32_t plspid;
};
struct nbkey_map_data {
struct nbkey_map_item mi;
struct lsp_nb_key nbkey;
uint32_t plspid;
};
struct req_map_data {
struct req_map_item mi;
struct lsp_nb_key nbkey;
uint32_t reqid;
};
struct req_entry {
RB_ENTRY(req_entry) entry;
struct thread *t_retry;
int retry_count;
bool was_sent;
struct path *path;
};
RB_HEAD(req_entry_head, req_entry);
RB_PROTOTYPE(req_entry_head, req_entry, entry, req_entry_compare);
struct pcc_state {
int id;
char tag[MAX_TAG_SIZE];
enum pcc_status status;
uint16_t flags;
#define F_PCC_STATE_HAS_IPV4 0x0002
#define F_PCC_STATE_HAS_IPV6 0x0004
struct pcc_opts *pcc_opts;
struct pce_opts *pce_opts;
struct in_addr pcc_addr_v4;
struct in6_addr pcc_addr_v6;
/* PCC transport source address */
struct ipaddr pcc_addr_tr;
char *originator;
pcep_session *sess;
uint32_t retry_count;
bool synchronized;
struct thread *t_reconnect;
struct thread *t_update_best;
struct thread *t_session_timeout;
uint32_t next_reqid;
uint32_t next_plspid;
struct plspid_map_head plspid_map;
struct nbkey_map_head nbkey_map;
struct req_map_head req_map;
struct req_entry_head requests;
struct pcep_caps caps;
bool is_best;
bool previous_best;
};
struct pcc_state *pcep_pcc_initialize(struct ctrl_state *ctrl_state,
int pcc_id);
void pcep_pcc_finalize(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state);
int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state);
int pcep_pcc_disable(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state);
int pcep_pcc_update(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state,
struct pcc_opts *pcc_opts, struct pce_opts *pce_opts);
void pcep_pcc_reconnect(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state);
void pcep_pcc_pcep_event_handler(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
pcep_event *event);
void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
enum pcep_pathd_event_type type,
struct path *path);
void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
enum pcep_ctrl_timer_type type, void *param);
void pcep_pcc_sync_path(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state, struct path *path);
void pcep_pcc_sync_done(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state);
void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state, struct path *path);
int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
struct pcc_state **pcc_state_list);
int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state,
struct pcc_state **pcc_state_list);
int pcep_pcc_timer_update_best_pce(struct ctrl_state *ctrl_state, int pcc_id);
int pcep_pcc_calculate_best_pce(struct pcc_state **pcc);
int pcep_pcc_get_pcc_id_by_ip_port(struct pcc_state **pcc,
struct pce_opts *pce_opts);
int pcep_pcc_get_pcc_id_by_idx(struct pcc_state **pcc, int idx);
struct pcc_state *pcep_pcc_get_pcc_by_id(struct pcc_state **pcc, int id);
struct pcc_state *pcep_pcc_get_pcc_by_name(struct pcc_state **pcc,
const char *pce_name);
int pcep_pcc_get_pcc_idx_by_id(struct pcc_state **pcc, int id);
int pcep_pcc_get_free_pcc_idx(struct pcc_state **pcc);
int pcep_pcc_get_pcc_id(struct pcc_state *pcc);
void pcep_pcc_copy_pcc_info(struct pcc_state **pcc,
struct pcep_pcc_info *pcc_info);
#endif // _PATH_PCEP_PCC_H_

296
pathd/path_zebra.c Normal file
View File

@ -0,0 +1,296 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "thread.h"
#include "log.h"
#include "lib_errors.h"
#include "if.h"
#include "prefix.h"
#include "zclient.h"
#include "network.h"
#include "stream.h"
#include "linklist.h"
#include "nexthop.h"
#include "vrf.h"
#include "typesafe.h"
#include "pathd/pathd.h"
#include "pathd/path_zebra.h"
static struct zclient *zclient;
static struct zclient *zclient_sync;
/* Global Variables */
bool g_has_router_id_v4 = false;
bool g_has_router_id_v6 = false;
struct in_addr g_router_id_v4;
struct in6_addr g_router_id_v6;
pthread_mutex_t g_router_id_v4_mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t g_router_id_v6_mtx = PTHREAD_MUTEX_INITIALIZER;
/**
* Gives the IPv4 router ID received from Zebra.
*
* @param router_id The in_addr strucure where to store the router id
* @return true if the router ID was available, false otherwise
*/
bool get_ipv4_router_id(struct in_addr *router_id)
{
bool retval = false;
assert(router_id != NULL);
pthread_mutex_lock(&g_router_id_v4_mtx);
if (g_has_router_id_v4) {
memcpy(router_id, &g_router_id_v4, sizeof(*router_id));
retval = true;
}
pthread_mutex_unlock(&g_router_id_v4_mtx);
return retval;
}
/**
* Gives the IPv6 router ID received from Zebra.
*
* @param router_id The in6_addr strucure where to store the router id
* @return true if the router ID was available, false otherwise
*/
bool get_ipv6_router_id(struct in6_addr *router_id)
{
bool retval = false;
assert(router_id != NULL);
pthread_mutex_lock(&g_router_id_v6_mtx);
if (g_has_router_id_v6) {
memcpy(router_id, &g_router_id_v6, sizeof(*router_id));
retval = true;
}
pthread_mutex_unlock(&g_router_id_v6_mtx);
return retval;
}
static void path_zebra_connected(struct zclient *zclient)
{
struct srte_policy *policy;
zclient_send_reg_requests(zclient, VRF_DEFAULT);
zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_ADD, AFI_IP6,
VRF_DEFAULT);
RB_FOREACH (policy, srte_policy_head, &srte_policies) {
struct srte_candidate *candidate;
struct srte_segment_list *segment_list;
candidate = policy->best_candidate;
if (!candidate)
continue;
segment_list = candidate->lsp->segment_list;
if (!segment_list)
continue;
path_zebra_add_sr_policy(policy, segment_list);
}
}
static int path_zebra_sr_policy_notify_status(ZAPI_CALLBACK_ARGS)
{
struct zapi_sr_policy zapi_sr_policy;
struct srte_policy *policy;
struct srte_candidate *best_candidate_path;
if (zapi_sr_policy_notify_status_decode(zclient->ibuf, &zapi_sr_policy))
return -1;
policy = srte_policy_find(zapi_sr_policy.color,
&zapi_sr_policy.endpoint);
if (!policy)
return -1;
best_candidate_path = policy->best_candidate;
if (!best_candidate_path)
return -1;
srte_candidate_status_update(best_candidate_path,
zapi_sr_policy.status);
return 0;
}
/* Router-id update message from zebra. */
static int path_zebra_router_id_update(ZAPI_CALLBACK_ARGS)
{
struct prefix pref;
const char *family;
char buf[PREFIX2STR_BUFFER];
zebra_router_id_update_read(zclient->ibuf, &pref);
if (pref.family == AF_INET) {
pthread_mutex_lock(&g_router_id_v4_mtx);
memcpy(&g_router_id_v4, &pref.u.prefix4,
sizeof(g_router_id_v4));
g_has_router_id_v4 = true;
inet_ntop(AF_INET, &g_router_id_v4, buf, sizeof(buf));
pthread_mutex_unlock(&g_router_id_v4_mtx);
family = "IPv4";
} else if (pref.family == AF_INET6) {
pthread_mutex_lock(&g_router_id_v6_mtx);
memcpy(&g_router_id_v6, &pref.u.prefix6,
sizeof(g_router_id_v6));
g_has_router_id_v6 = true;
inet_ntop(AF_INET6, &g_router_id_v6, buf, sizeof(buf));
pthread_mutex_unlock(&g_router_id_v6_mtx);
family = "IPv6";
} else {
pthread_mutex_unlock(&g_router_id_v4_mtx);
zlog_warn("Unexpected router ID address family for vrf %u: %u",
vrf_id, pref.family);
return 0;
}
pthread_mutex_unlock(&g_router_id_v4_mtx);
zlog_info("%s Router Id updated for VRF %u: %s", family, vrf_id, buf);
return 0;
}
/**
* Adds a segment routing policy to Zebra.
*
* @param policy The policy to add
* @param segment_list The segment list for the policy
*/
void path_zebra_add_sr_policy(struct srte_policy *policy,
struct srte_segment_list *segment_list)
{
struct zapi_sr_policy zp = {};
struct srte_segment_entry *segment;
zp.color = policy->color;
zp.endpoint = policy->endpoint;
strlcpy(zp.name, policy->name, sizeof(zp.name));
zp.segment_list.type = ZEBRA_LSP_SRTE;
zp.segment_list.local_label = policy->binding_sid;
zp.segment_list.label_num = 0;
RB_FOREACH (segment, srte_segment_entry_head, &segment_list->segments)
zp.segment_list.labels[zp.segment_list.label_num++] =
segment->sid_value;
policy->status = SRTE_POLICY_STATUS_GOING_UP;
(void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_SET, &zp);
}
/**
* Deletes a segment policy from Zebra.
*
* @param policy The policy to remove
*/
void path_zebra_delete_sr_policy(struct srte_policy *policy)
{
struct zapi_sr_policy zp = {};
zp.color = policy->color;
zp.endpoint = policy->endpoint;
strlcpy(zp.name, policy->name, sizeof(zp.name));
zp.segment_list.type = ZEBRA_LSP_SRTE;
zp.segment_list.local_label = policy->binding_sid;
zp.segment_list.label_num = 0;
policy->status = SRTE_POLICY_STATUS_DOWN;
(void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_DELETE, &zp);
}
/**
* Allocates a label from Zebra's label manager.
*
* @param label the label to be allocated
* @return 0 if the label has been allocated, -1 otherwise
*/
int path_zebra_request_label(mpls_label_t label)
{
int ret;
uint32_t start, end;
ret = lm_get_label_chunk(zclient_sync, 0, label, 1, &start, &end);
if (ret < 0) {
zlog_warn("%s: error getting label range!", __func__);
return -1;
}
return 0;
}
/**
* Releases a previously allocated label from Zebra's label manager.
*
* @param label The label to release
* @return 0 ifthe label has beel released, -1 otherwise
*/
void path_zebra_release_label(mpls_label_t label)
{
int ret;
ret = lm_release_label_chunk(zclient_sync, label, label);
if (ret < 0)
zlog_warn("%s: error releasing label range!", __func__);
}
static void path_zebra_label_manager_connect(void)
{
/* Connect to label manager. */
while (zclient_socket_connect(zclient_sync) < 0) {
zlog_warn("%s: error connecting synchronous zclient!",
__func__);
sleep(1);
}
set_nonblocking(zclient_sync->sock);
/* Send hello to notify zebra this is a synchronous client */
while (zclient_send_hello(zclient_sync) < 0) {
zlog_warn("%s: Error sending hello for synchronous zclient!",
__func__);
sleep(1);
}
while (lm_label_manager_connect(zclient_sync, 0) != 0) {
zlog_warn("%s: error connecting to label manager!", __func__);
sleep(1);
}
}
/**
* Initializes Zebra asynchronous connection.
*
* @param master The master thread
*/
void path_zebra_init(struct thread_master *master)
{
struct zclient_options options = zclient_options_default;
options.synchronous = true;
/* Initialize asynchronous zclient. */
zclient = zclient_new(master, &zclient_options_default);
zclient_init(zclient, ZEBRA_ROUTE_SRTE, 0, &pathd_privs);
zclient->zebra_connected = path_zebra_connected;
zclient->sr_policy_notify_status = path_zebra_sr_policy_notify_status;
zclient->router_id_update = path_zebra_router_id_update;
/* Initialize special zclient for synchronous message exchanges. */
zclient_sync = zclient_new(master, &options);
zclient_sync->sock = -1;
zclient_sync->redist_default = ZEBRA_ROUTE_SRTE;
zclient_sync->instance = 1;
zclient_sync->privs = &pathd_privs;
/* Connect to the LM. */
path_zebra_label_manager_connect();
}

34
pathd/path_zebra.h Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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_PATH_MPLS_H_
#define _FRR_PATH_MPLS_H_
#include <zebra.h>
#include "pathd/pathd.h"
bool get_ipv4_router_id(struct in_addr *router_id);
bool get_ipv6_router_id(struct in6_addr *router_id);
void path_zebra_add_sr_policy(struct srte_policy *policy,
struct srte_segment_list *segment_list);
void path_zebra_delete_sr_policy(struct srte_policy *policy);
int path_zebra_request_label(mpls_label_t label);
void path_zebra_release_label(mpls_label_t label);
void path_zebra_init(struct thread_master *master);
#endif /* _FRR_PATH_MPLS_H_ */

1127
pathd/pathd.c Normal file

File diff suppressed because it is too large Load Diff

41
pathd/pathd.conf.sample Normal file
View File

@ -0,0 +1,41 @@
! Default pathd configuration sample
!
password frr
log stdout
segment-routing
traffic-eng
segment-list test1
index 10 mpls label 123
index 20 mpls label 456
!
segment-list test2
index 10 mpls label 321
index 20 mpls label 654
!
policy color 1 endpoint 1.1.1.1
name one
binding-sid 100
candidate-path preference 100 name test1 explicit segment-list test1
candidate-path preference 200 name test2 explicit segment-list test2
!
policy color 2 endpoint 2.2.2.2
name two
binding-sid 101
candidate-path preference 100 name def explicit segment-list test2
candidate-path preference 200 name dyn dynamic
bandwidth 12345
metric bound abc 16 required
metric te 10
!
!
pcep
pcc-peer PCE1
address ip 127.0.0.1
sr-draft07
!
pcc
peer PCE1
!
!
!

412
pathd/pathd.h Normal file
View File

@ -0,0 +1,412 @@
/*
* Copyright (C) 2020 NetDEF, Inc.
*
* This program 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 of the License, or (at your option)
* any later version.
*
* 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; 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_PATHD_H_
#define _FRR_PATHD_H_
#include "lib/mpls.h"
#include "lib/ipaddr.h"
#include "lib/srte.h"
#include "lib/hook.h"
enum srte_protocol_origin {
SRTE_ORIGIN_UNDEFINED = 0,
SRTE_ORIGIN_PCEP = 1,
SRTE_ORIGIN_BGP = 2,
SRTE_ORIGIN_LOCAL = 3,
};
enum srte_policy_status {
SRTE_POLICY_STATUS_UNKNOWN = 0,
SRTE_POLICY_STATUS_DOWN = 1,
SRTE_POLICY_STATUS_UP = 2,
SRTE_POLICY_STATUS_GOING_DOWN = 3,
SRTE_POLICY_STATUS_GOING_UP = 4
};
enum srte_candidate_type {
SRTE_CANDIDATE_TYPE_UNDEFINED = 0,
SRTE_CANDIDATE_TYPE_EXPLICIT = 1,
SRTE_CANDIDATE_TYPE_DYNAMIC = 2,
};
enum srte_candidate_metric_type {
/* IGP metric */
SRTE_CANDIDATE_METRIC_TYPE_IGP = 1,
/* TE metric */
SRTE_CANDIDATE_METRIC_TYPE_TE = 2,
/* Hop Counts */
SRTE_CANDIDATE_METRIC_TYPE_HC = 3,
/* Aggregate bandwidth consumption */
SRTE_CANDIDATE_METRIC_TYPE_ABC = 4,
/* Load of the most loaded link */
SRTE_CANDIDATE_METRIC_TYPE_LMLL = 5,
/* Cumulative IGP cost */
SRTE_CANDIDATE_METRIC_TYPE_CIGP = 6,
/* Cumulative TE cost */
SRTE_CANDIDATE_METRIC_TYPE_CTE = 7,
/* P2MP IGP metric */
SRTE_CANDIDATE_METRIC_TYPE_PIGP = 8,
/* P2MP TE metric */
SRTE_CANDIDATE_METRIC_TYPE_PTE = 9,
/* P2MP hop count metric */
SRTE_CANDIDATE_METRIC_TYPE_PHC = 10,
/* Segment-ID (SID) Depth */
SRTE_CANDIDATE_METRIC_TYPE_MSD = 11,
/* Path Delay metric */
SRTE_CANDIDATE_METRIC_TYPE_PD = 12,
/* Path Delay Variation metric */
SRTE_CANDIDATE_METRIC_TYPE_PDV = 13,
/* Path Loss metric */
SRTE_CANDIDATE_METRIC_TYPE_PL = 14,
/* P2MP Path Delay metric */
SRTE_CANDIDATE_METRIC_TYPE_PPD = 15,
/* P2MP Path Delay variation metric */
SRTE_CANDIDATE_METRIC_TYPE_PPDV = 16,
/* P2MP Path Loss metric */
SRTE_CANDIDATE_METRIC_TYPE_PPL = 17,
/* Number of adaptations on a path */
SRTE_CANDIDATE_METRIC_TYPE_NAP = 18,
/* Number of layers on a path */
SRTE_CANDIDATE_METRIC_TYPE_NLP = 19,
/* Domain Count metric */
SRTE_CANDIDATE_METRIC_TYPE_DC = 20,
/* Border Node Count metric */
SRTE_CANDIDATE_METRIC_TYPE_BNC = 21,
};
#define MAX_METRIC_TYPE 21
enum srte_segment_nai_type {
SRTE_SEGMENT_NAI_TYPE_NONE = 0,
SRTE_SEGMENT_NAI_TYPE_IPV4_NODE = 1,
SRTE_SEGMENT_NAI_TYPE_IPV6_NODE = 2,
SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY = 3,
SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY = 4,
SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY = 5
};
enum objfun_type {
OBJFUN_UNDEFINED = 0,
/* Minimum Cost Path [RFC5541] */
OBJFUN_MCP = 1,
/* Minimum Load Path [RFC5541] */
OBJFUN_MLP = 2,
/* Maximum residual Bandwidth Path [RFC5541] */
OBJFUN_MBP = 3,
/* Minimize aggregate Bandwidth Consumption [RFC5541] */
OBJFUN_MBC = 4,
/* Minimize the Load of the most loaded Link [RFC5541] */
OBJFUN_MLL = 5,
/* Minimize the Cumulative Cost of a set of paths [RFC5541] */
OBJFUN_MCC = 6,
/* Shortest Path Tree [RFC8306] */
OBJFUN_SPT = 7,
/* Minimum Cost Tree [RFC8306] */
OBJFUN_MCT = 8,
/* Minimum Packet Loss Path [RFC8233] */
OBJFUN_MPLP = 9,
/* Maximum Under-Utilized Path [RFC8233] */
OBJFUN_MUP = 10,
/* Maximum Reserved Under-Utilized Path [RFC8233] */
OBJFUN_MRUP = 11,
/* Minimize the number of Transit Domains [RFC8685] */
OBJFUN_MTD = 12,
/* Minimize the number of Border Nodes [RFC8685] */
OBJFUN_MBN = 13,
/* Minimize the number of Common Transit Domains [RFC8685] */
OBJFUN_MCTD = 14,
/* Minimize the number of Shared Links [RFC8800] */
OBJFUN_MSL = 15,
/* Minimize the number of Shared SRLGs [RFC8800] */
OBJFUN_MSS = 16,
/* Minimize the number of Shared Nodes [RFC8800] */
OBJFUN_MSN = 17,
};
#define MAX_OBJFUN_TYPE 17
enum affinity_filter_type {
AFFINITY_FILTER_UNDEFINED = 0,
AFFINITY_FILTER_EXCLUDE_ANY = 1,
AFFINITY_FILTER_INCLUDE_ANY = 2,
AFFINITY_FILTER_INCLUDE_ALL = 3,
};
#define MAX_AFFINITY_FILTER_TYPE 3
struct srte_segment_list;
struct srte_segment_entry {
RB_ENTRY(srte_segment_entry) entry;
/* The segment list the entry belong to */
struct srte_segment_list *segment_list;
/* Index of the Label. */
uint32_t index;
/* Label Value. */
mpls_label_t sid_value;
/* NAI Type */
enum srte_segment_nai_type nai_type;
/* NAI local address when nai type is not NONE */
struct ipaddr nai_local_addr;
/* NAI local interface when nai type is not IPv4 unnumbered adjacency */
uint32_t nai_local_iface;
/* NAI local interface when nai type is IPv4 or IPv6 adjacency */
struct ipaddr nai_remote_addr;
/* NAI remote interface when nai type is not IPv4 unnumbered adjacency
*/
uint32_t nai_remote_iface;
};
RB_HEAD(srte_segment_entry_head, srte_segment_entry);
RB_PROTOTYPE(srte_segment_entry_head, srte_segment_entry, entry,
srte_segment_entry_compare)
struct srte_segment_list {
RB_ENTRY(srte_segment_list) entry;
/* Name of the Segment List. */
char name[64];
/* The Protocol-Origin. */
enum srte_protocol_origin protocol_origin;
/* The Originator */
char originator[64];
/* Nexthops. */
struct srte_segment_entry_head segments;
/* Status flags. */
uint16_t flags;
#define F_SEGMENT_LIST_NEW 0x0002
#define F_SEGMENT_LIST_MODIFIED 0x0004
#define F_SEGMENT_LIST_DELETED 0x0008
};
RB_HEAD(srte_segment_list_head, srte_segment_list);
RB_PROTOTYPE(srte_segment_list_head, srte_segment_list, entry,
srte_segment_list_compare)
struct srte_metric {
uint16_t flags;
#define F_METRIC_IS_DEFINED 0x0001
#define F_METRIC_IS_REQUIRED 0x0002
#define F_METRIC_IS_BOUND 0x0004
#define F_METRIC_IS_COMPUTED 0x0008
float value;
};
/* Runtime information about the candidate path */
struct srte_lsp {
/* Backpointer to the Candidate Path. */
struct srte_candidate *candidate;
/* The associated Segment List. */
struct srte_segment_list *segment_list;
/* The Protocol-Origin. */
enum srte_protocol_origin protocol_origin;
/* The Originator */
char originator[64];
/* The Discriminator */
uint32_t discriminator;
/* Flags. */
uint32_t flags;
/* Metrics LSP Values */
struct srte_metric metrics[MAX_METRIC_TYPE];
/* Bandwidth Configured Value */
float bandwidth;
/* The objective function in used */
enum objfun_type objfun;
};
/* Configured candidate path */
struct srte_candidate {
RB_ENTRY(srte_candidate) entry;
/* Backpointer to SR Policy */
struct srte_policy *policy;
/* The LSP associated with this candidate path. */
struct srte_lsp *lsp;
/* Administrative preference. */
uint32_t preference;
/* Symbolic Name. */
char name[64];
/* The associated Segment List. */
struct srte_segment_list *segment_list;
/* The Protocol-Origin. */
enum srte_protocol_origin protocol_origin;
/* The Originator */
char originator[64];
/* The Discriminator */
uint32_t discriminator;
/* The Type (explicit or dynamic) */
enum srte_candidate_type type;
/* Flags. */
uint32_t flags;
#define F_CANDIDATE_BEST 0x0001
#define F_CANDIDATE_NEW 0x0002
#define F_CANDIDATE_MODIFIED 0x0004
#define F_CANDIDATE_DELETED 0x0008
#define F_CANDIDATE_HAS_BANDWIDTH 0x0100
#define F_CANDIDATE_REQUIRED_BANDWIDTH 0x0200
#define F_CANDIDATE_HAS_OBJFUN 0x0400
#define F_CANDIDATE_REQUIRED_OBJFUN 0x0800
#define F_CANDIDATE_HAS_EXCLUDE_ANY 0x1000
#define F_CANDIDATE_HAS_INCLUDE_ANY 0x2000
#define F_CANDIDATE_HAS_INCLUDE_ALL 0x4000
/* Metrics Configured Values */
struct srte_metric metrics[MAX_METRIC_TYPE];
/* Bandwidth Configured Value */
float bandwidth;
/* Configured objective functions */
enum objfun_type objfun;
/* Path constraints attribute filters */
uint32_t affinity_filters[MAX_AFFINITY_FILTER_TYPE];
/* Hooks delaying timer */
struct thread *hook_timer;
};
RB_HEAD(srte_candidate_head, srte_candidate);
RB_PROTOTYPE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare)
struct srte_policy {
RB_ENTRY(srte_policy) entry;
/* Color */
uint32_t color;
/* Endpoint */
struct ipaddr endpoint;
/* Name */
char name[64];
/* Binding SID */
mpls_label_t binding_sid;
/* Operational Status of the policy */
enum srte_policy_status status;
/* Best candidate path. */
struct srte_candidate *best_candidate;
/* Candidate Paths */
struct srte_candidate_head candidate_paths;
/* Status flags. */
uint16_t flags;
#define F_POLICY_NEW 0x0002
#define F_POLICY_MODIFIED 0x0004
#define F_POLICY_DELETED 0x0008
};
RB_HEAD(srte_policy_head, srte_policy);
RB_PROTOTYPE(srte_policy_head, srte_policy, entry, srte_policy_compare)
DECLARE_HOOK(pathd_candidate_created, (struct srte_candidate * candidate),
(candidate))
DECLARE_HOOK(pathd_candidate_updated, (struct srte_candidate * candidate),
(candidate))
DECLARE_HOOK(pathd_candidate_removed, (struct srte_candidate * candidate),
(candidate))
extern struct srte_segment_list_head srte_segment_lists;
extern struct srte_policy_head srte_policies;
extern struct zebra_privs_t pathd_privs;
/* master thread, defined in path_main.c */
extern struct thread_master *master;
/* pathd.c */
struct srte_segment_list *srte_segment_list_add(const char *name);
void srte_segment_list_del(struct srte_segment_list *segment_list);
struct srte_segment_list *srte_segment_list_find(const char *name);
struct srte_segment_entry *
srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index);
void srte_segment_entry_del(struct srte_segment_entry *segment);
void srte_segment_entry_set_nai(struct srte_segment_entry *segment,
enum srte_segment_nai_type type,
struct ipaddr *local_ip, uint32_t local_iface,
struct ipaddr *remote_ip,
uint32_t remote_iface);
struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint);
void srte_policy_del(struct srte_policy *policy);
struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint);
void srte_policy_update_binding_sid(struct srte_policy *policy,
uint32_t binding_sid);
void srte_apply_changes(void);
void srte_policy_apply_changes(struct srte_policy *policy);
struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
uint32_t preference);
void srte_candidate_del(struct srte_candidate *candidate);
void srte_candidate_set_bandwidth(struct srte_candidate *candidate,
float bandwidth, bool required);
void srte_candidate_unset_bandwidth(struct srte_candidate *candidate);
void srte_candidate_set_metric(struct srte_candidate *candidate,
enum srte_candidate_metric_type type,
float value, bool required, bool is_cound,
bool is_computed);
void srte_candidate_unset_metric(struct srte_candidate *candidate,
enum srte_candidate_metric_type type);
void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required,
enum objfun_type type);
void srte_candidate_unset_objfun(struct srte_candidate *candidate);
void srte_candidate_set_affinity_filter(struct srte_candidate *candidate,
enum affinity_filter_type type,
uint32_t filter);
void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate,
enum affinity_filter_type type);
void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth,
bool required);
void srte_lsp_unset_bandwidth(struct srte_lsp *lsp);
void srte_lsp_set_metric(struct srte_lsp *lsp,
enum srte_candidate_metric_type type, float value,
bool required, bool is_cound, bool is_computed);
void srte_lsp_unset_metric(struct srte_lsp *lsp,
enum srte_candidate_metric_type type);
struct srte_candidate *srte_candidate_find(struct srte_policy *policy,
uint32_t preference);
struct srte_segment_entry *
srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index);
void srte_candidate_status_update(struct srte_candidate *candidate, int status);
void srte_candidate_unset_segment_list(const char *originator, bool force);
const char *srte_origin2str(enum srte_protocol_origin origin);
/* path_cli.c */
void path_cli_init(void);
#endif /* _FRR_PATHD_H_ */

79
pathd/subdir.am Normal file
View File

@ -0,0 +1,79 @@
#
# pathd
#
if PATHD
noinst_LIBRARIES += pathd/libpath.a
sbin_PROGRAMS += pathd/pathd
dist_examples_DATA += pathd/pathd.conf.sample
vtysh_scan += $(top_srcdir)/pathd/path_cli.c
vtysh_daemons += pathd
# TODO add man page
#man8 += $(MANBUILD)/pathd.8
if HAVE_PATHD_PCEP
vtysh_scan += $(top_srcdir)/pathd/path_pcep_cli.c
module_LTLIBRARIES += pathd/pathd_pcep.la
endif
endif
pathd_libpath_a_SOURCES = \
pathd/path_cli.c \
pathd/path_debug.c \
pathd/path_errors.c \
pathd/path_main.c \
pathd/path_memory.c \
pathd/path_nb.c \
pathd/path_nb_config.c \
pathd/path_nb_state.c \
pathd/path_zebra.c \
pathd/pathd.c \
# end
clippy_scan += \
pathd/path_cli.c \
# end
if HAVE_PATHD_PCEP
clippy_scan += \
pathd/path_pcep_cli.c \
# end
endif
noinst_HEADERS += \
pathd/path_debug.h \
pathd/path_errors.h \
pathd/path_memory.h \
pathd/path_nb.h \
pathd/path_pcep.h \
pathd/path_pcep_cli.h \
pathd/path_pcep_controller.h \
pathd/path_pcep_debug.h \
pathd/path_pcep_lib.h \
pathd/path_pcep_memory.h \
pathd/path_pcep_config.h \
pathd/path_pcep_pcc.h \
pathd/path_zebra.h \
pathd/pathd.h \
# end
pathd_pathd_SOURCES = pathd/path_main.c
nodist_pathd_pathd_SOURCES = \
yang/frr-pathd.yang.c \
# end
pathd_pathd_LDADD = pathd/libpath.a lib/libfrr.la -lm $(LIBCAP)
pathd_pathd_pcep_la_SOURCES = \
pathd/path_pcep.c \
pathd/path_pcep_cli.c \
pathd/path_pcep_controller.c \
pathd/path_pcep_debug.c \
pathd/path_pcep_lib.c \
pathd/path_pcep_memory.c \
pathd/path_pcep_config.c \
pathd/path_pcep_pcc.c \
# end
pathd_pathd_pcep_la_CFLAGS = $(WERROR)
pathd_pathd_pcep_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
pathd_pathd_pcep_la_LIBADD = @PATHD_PCEP_LIBS@

View File

@ -27,6 +27,7 @@
%{!?with_vrrpd: %global with_vrrpd 1 }
%{!?with_rtadv: %global with_rtadv 1 }
%{!?with_watchfrr: %global with_watchfrr 1 }
%{!?with_pathd: %global with_pathd 1 }
# user and group
%{!?frr_user: %global frr_user frr }
@ -87,7 +88,7 @@
%{!?frr_gid: %global frr_gid 92 }
%{!?vty_gid: %global vty_gid 85 }
%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd staticd bfdd fabricd
%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd staticd bfdd fabricd pathd
%if %{with_ldpd}
%define daemon_ldpd ldpd
@ -143,7 +144,13 @@
%define daemon_bfdd ""
%endif
%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd}
%if %{with_pathd}
%define daemon_pathd pathd
%else
%define daemon_pathd ""
%endif
%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd} %{daemon_pathd}
#release sub-revision (the two digits after the CONFDATE)
%{!?release_rev: %global release_rev 01 }
@ -397,6 +404,11 @@ routing state through standard SNMP MIBs.
--enable-bfdd \
%else
--disable-bfdd \
%endif
%if %{with_pathd}
--enable-pathd \
%else
--disable-pathd \
%endif
--enable-snmp
# end
@ -526,6 +538,9 @@ zebra_spec_add_service fabricd 2618/tcp "Fabricd vty"
%if %{with_vrrpd}
zebra_spec_add_service vrrpd 2619/tcp "VRRPd vty"
%endif
%if %{with_pathd}
zebra_spec_add_service pathd 2620/tcp "Pathd vty"
%endif
%if "%{initsystem}" == "systemd"
for daemon in %all_daemons ; do
@ -681,6 +696,9 @@ fi
%if %{with_bfdd}
%{_sbindir}/bfdd
%endif
%if %{with_pathd}
%{_sbindir}/pathd
%endif
%{_libdir}/libfrr.so*
%{_libdir}/libfrrcares*
%{_libdir}/libfrrospf*
@ -798,6 +816,9 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons
- migrate route-maps to use northbound interface
- plus countless bug fixes and other improvements
* Mon Jun 15 2020 Sascha Kattelmann <sascha@netdef.org>
- Add Pathd support
* Wed May 06 2020 David Lamparter <equinox@opensourcerouting.org> - 7.3.1
- upstream 7.3.1

View File

@ -0,0 +1,19 @@
log file zebra.log
!
hostname dst
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 9.9.9.2/32
ipv6 address 2001:db8:1066::2/128
!
interface eth-rt6
ip address 10.0.11.2/24
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,16 @@
log file bgpd.log
!
router bgp 1
bgp router-id 1.1.1.1
neighbor 6.6.6.6 remote-as 1
neighbor 6.6.6.6 update-source lo
!
address-family ipv4 unicast
redistribute static
neighbor 6.6.6.6 next-hop-self
neighbor 6.6.6.6 route-map SET_SR_POLICY in
exit-address-family
!
route-map SET_SR_POLICY permit 10
set sr-te color 1
!

View File

@ -0,0 +1,30 @@
password 1
hostname rt1
log file isisd.log
!
debug isis events
debug isis route-events
debug isis spf-events
debug isis sr-events
debug isis lsp-gen
!
interface lo
ip router isis 1
ipv6 router isis 1
isis passive
!
interface eth-sw1
ip router isis 1
ipv6 router isis 1
isis hello-multiplier 3
!
router isis 1
net 49.0000.0000.0000.0001.00
is-type level-1
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
segment-routing node-msd 8
segment-routing prefix 1.1.1.1/32 index 10
segment-routing prefix 2001:db8:1000::1/128 index 11
!

View File

@ -0,0 +1,21 @@
log file pathd.log
!
hostname rt1
!
segment-routing
traffic-eng
segment-list default
index 10 mpls label 16050
index 20 mpls label 16060
!
segment-list test
index 10 mpls label 16020
index 20 mpls label 16040
index 30 mpls label 16060
!
policy color 1 endpoint 6.6.6.6
name default
binding-sid 1111
!
!
!

View File

@ -0,0 +1,91 @@
{
"1111":{
"inLabel":1111,
"installed":true,
"nexthops":[
{
"type":"SR-TE",
"outLabel":16050,
"outLabelStack":[
16050,
16060
],
"distance":150,
"installed":true,
"nexthop":"10.0.1.3"
}
]
},
"16020":{
"inLabel":16020,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16020,
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
},
"16030":{
"inLabel":16030,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16030,
"distance":150,
"installed":true,
"nexthop":"10.0.1.3"
}
]
},
"16040":{
"inLabel":16040,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16040,
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
},
"16050":{
"inLabel":16050,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16050,
"distance":150,
"installed":true,
"nexthop":"10.0.1.3"
}
]
},
"16060":{
"inLabel":16060,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16060,
"distance":150,
"installed":true,
"nexthop":"10.0.1.3"
},
{
"type":"SR (IS-IS)",
"outLabel":16060,
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
}
}

View File

@ -0,0 +1,74 @@
{
"16020":{
"inLabel":16020,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16020,
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
},
"16030":{
"inLabel":16030,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16030,
"distance":150,
"installed":true,
"nexthop":"10.0.1.3"
}
]
},
"16040":{
"inLabel":16040,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16040,
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
},
"16050":{
"inLabel":16050,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16050,
"distance":150,
"installed":true,
"nexthop":"10.0.1.3"
}
]
},
"16060":{
"inLabel":16060,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16060,
"distance":150,
"installed":true,
"nexthop":"10.0.1.3"
},
{
"type":"SR (IS-IS)",
"outLabel":16060,
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
}
}

View File

@ -0,0 +1,13 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": false
}
]
}
}
}

View File

@ -0,0 +1,20 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"discriminator": "*",
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,20 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"discriminator": "*",
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,25 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"discriminator": "*",
"is-best-candidate-path": false
},
{
"preference": 200,
"discriminator": "*",
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,20 @@
{
"1111":{
"inLabel":1111,
"installed":true,
"nexthops":[
{
"type":"SR-TE",
"outLabel":16020,
"outLabelStack":[
16020,
16040,
16060
],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
}
}

View File

@ -0,0 +1,21 @@
{
"1111":{
"inLabel":1111,
"installed":true,
"nexthops":[
{
"type":"SR-TE",
"outLabel":16020,
"outLabelStack":[
16020,
16040,
16050,
16060
],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
}
}

View File

@ -0,0 +1,21 @@
{
"1111":{
"inLabel":1111,
"installed":true,
"nexthops":[
{
"type":"SR-TE",
"outLabel":16020,
"outLabelStack":[
16020,
16040,
16030,
16060
],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
}
]
}
}

View File

@ -0,0 +1,29 @@
{
"9.9.9.2\/32":[
{
"prefix":"9.9.9.2\/32",
"protocol":"bgp",
"installed":true,
"nexthops":[
{
"ip":"6.6.6.6",
"afi":"ipv4",
"active":true,
"recursive":true,
"srteColor":1
},
{
"fib":true,
"ip":"10.0.1.3",
"afi":"ipv4",
"interfaceName":"eth-sw1",
"active":true,
"labels":[
16050,
16060
]
}
]
}
]
}

View File

@ -0,0 +1,38 @@
{
"9.9.9.2\/32":[
{
"prefix":"9.9.9.2\/32",
"protocol":"bgp",
"installed":true,
"nexthops":[
{
"ip":"6.6.6.6",
"afi":"ipv4",
"active":true,
"recursive":true,
"srteColor":1
},
{
"fib":true,
"ip":"10.0.1.2",
"afi":"ipv4",
"interfaceName":"eth-sw1",
"active":true,
"labels":[
16060
]
},
{
"fib":true,
"ip":"10.0.1.3",
"afi":"ipv4",
"interfaceName":"eth-sw1",
"active":true,
"labels":[
16060
]
}
]
}
]
}

View File

@ -0,0 +1,20 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"discriminator": "*",
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,20 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": false,
"candidate-path": [
{
"preference": 100,
"discriminator": "*",
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,19 @@
log file zebra.log
!
hostname rt1
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 1.1.1.1/32
ipv6 address 2001:db8:1000::1/128
!
interface eth-sw1
ip address 10.0.1.1/24
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,41 @@
hostname rt2
log file isisd.log
!
debug isis events
debug isis route-events
debug isis spf-events
debug isis sr-events
debug isis lsp-gen
!
interface lo
ip router isis 1
ipv6 router isis 1
isis passive
!
interface eth-sw1
ip router isis 1
ipv6 router isis 1
isis hello-multiplier 3
!
interface eth-rt4-1
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt4-2
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
router isis 1
net 49.0000.0000.0000.0002.00
is-type level-1
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
segment-routing node-msd 8
segment-routing prefix 2.2.2.2/32 index 20 no-php-flag
segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag
!

View File

@ -0,0 +1,25 @@
log file zebra.log
!
hostname rt2
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 2.2.2.2/32
ipv6 address 2001:db8:1000::2/128
!
interface eth-sw1
ip address 10.0.1.2/24
!
interface eth-rt4-1
ip address 10.0.2.2/24
!
interface eth-rt4-2
ip address 10.0.3.2/24
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,41 @@
hostname rt3
log file isisd.log
!
debug isis events
debug isis route-events
debug isis spf-events
debug isis sr-events
debug isis lsp-gen
!
interface lo
ip router isis 1
ipv6 router isis 1
isis passive
!
interface eth-sw1
ip router isis 1
ipv6 router isis 1
isis hello-multiplier 3
!
interface eth-rt5-1
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt5-2
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
router isis 1
net 49.0000.0000.0000.0003.00
is-type level-1
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
segment-routing node-msd 8
segment-routing prefix 3.3.3.3/32 index 30 no-php-flag
segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag
!

View File

@ -0,0 +1,25 @@
log file zebra.log
!
hostname rt3
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 3.3.3.3/32
ipv6 address 2001:db8:1000::3/128
!
interface eth-sw1
ip address 10.0.1.3/24
!
interface eth-rt5-1
ip address 10.0.4.3/24
!
interface eth-rt5-2
ip address 10.0.5.3/24
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,48 @@
hostname rt4
log file isisd.log
!
debug isis events
debug isis route-events
debug isis spf-events
debug isis sr-events
debug isis lsp-gen
!
interface lo
ip router isis 1
ipv6 router isis 1
isis passive
!
interface eth-rt2-1
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt2-2
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt5
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt6
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
router isis 1
net 49.0000.0000.0000.0004.00
is-type level-1
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
segment-routing node-msd 8
segment-routing prefix 4.4.4.4/32 index 40 no-php-flag
segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag
!

View File

@ -0,0 +1,28 @@
log file zebra.log
!
hostname rt4
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 4.4.4.4/32
ipv6 address 2001:db8:1000::4/128
!
interface eth-rt2-1
ip address 10.0.2.4/24
!
interface eth-rt2-2
ip address 10.0.3.4/24
!
interface eth-rt5
ip address 10.0.6.4/24
!
interface eth-rt6
ip address 10.0.7.4/24
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,48 @@
hostname rt5
log file isisd.log
!
debug isis events
debug isis route-events
debug isis spf-events
debug isis sr-events
debug isis lsp-gen
!
interface lo
ip router isis 1
ipv6 router isis 1
isis passive
!
interface eth-rt3-1
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt3-2
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt4
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt6
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
router isis 1
net 49.0000.0000.0000.0005.00
is-type level-1
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
segment-routing node-msd 8
segment-routing prefix 5.5.5.5/32 index 50 no-php-flag
segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag
!

View File

@ -0,0 +1,28 @@
log file zebra.log
!
hostname rt5
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 5.5.5.5/32
ipv6 address 2001:db8:1000::5/128
!
interface eth-rt3-1
ip address 10.0.4.5/24
!
interface eth-rt3-2
ip address 10.0.5.5/24
!
interface eth-rt4
ip address 10.0.6.5/24
!
interface eth-rt6
ip address 10.0.8.5/24
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,12 @@
log file bgpd.log
!
router bgp 1
bgp router-id 6.6.6.6
neighbor 1.1.1.1 remote-as 1
neighbor 1.1.1.1 update-source lo
!
address-family ipv4 unicast
redistribute static
neighbor 1.1.1.1 next-hop-self
exit-address-family
!

View File

@ -0,0 +1,36 @@
hostname rt6
log file isisd.log
!
debug isis events
debug isis route-events
debug isis spf-events
debug isis sr-events
debug isis lsp-gen
!
interface lo
ip router isis 1
ipv6 router isis 1
isis passive
!
interface eth-rt4
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
interface eth-rt5
ip router isis 1
ipv6 router isis 1
isis network point-to-point
isis hello-multiplier 3
!
router isis 1
net 49.0000.0000.0000.0006.00
is-type level-1
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
segment-routing node-msd 8
segment-routing prefix 6.6.6.6/32 index 60
segment-routing prefix 2001:db8:1000::6/128 index 61
!

View File

@ -0,0 +1,21 @@
log file pathd.log
!
hostname rt6
!
segment-routing
traffic-eng
segment-list default
index 10 mpls label 16020
index 20 mpls label 16010
!
segment-list test
index 10 mpls label 16050
index 20 mpls label 16030
index 30 mpls label 16010
!
policy color 1 endpoint 1.1.1.1
name default
binding-sid 6666
!
!
!

View File

@ -0,0 +1,91 @@
{
"6666":{
"inLabel":6666,
"installed":true,
"nexthops":[
{
"type":"SR-TE",
"outLabel":16020,
"outLabelStack":[
16020,
16010
],
"distance":150,
"installed":true,
"nexthop":"10.0.7.4"
}
]
},
"16010": {
"inLabel": 16010,
"installed": true,
"nexthops": [
{
"distance": 150,
"installed": true,
"nexthop": "10.0.7.4",
"outLabel": 16010,
"type": "SR (IS-IS)"
},
{
"distance": 150,
"installed": true,
"nexthop": "10.0.8.5",
"outLabel": 16010,
"type": "SR (IS-IS)"
}
]
},
"16020":{
"inLabel":16020,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16020,
"distance":150,
"installed":true,
"nexthop":"10.0.7.4"
}
]
},
"16030":{
"inLabel":16030,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16030,
"distance":150,
"installed":true,
"nexthop":"10.0.8.5"
}
]
},
"16040":{
"inLabel":16040,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16040,
"distance":150,
"installed":true,
"nexthop":"10.0.7.4"
}
]
},
"16050":{
"inLabel":16050,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16050,
"distance":150,
"installed":true,
"nexthop":"10.0.8.5"
}
]
}
}

View File

@ -0,0 +1,74 @@
{
"16010": {
"inLabel": 16010,
"installed": true,
"nexthops": [
{
"distance": 150,
"installed": true,
"nexthop": "10.0.7.4",
"outLabel": 16010,
"type": "SR (IS-IS)"
},
{
"distance": 150,
"installed": true,
"nexthop": "10.0.8.5",
"outLabel": 16010,
"type": "SR (IS-IS)"
}
]
},
"16020":{
"inLabel":16020,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16020,
"distance":150,
"installed":true,
"nexthop":"10.0.7.4"
}
]
},
"16030":{
"inLabel":16030,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16030,
"distance":150,
"installed":true,
"nexthop":"10.0.8.5"
}
]
},
"16040":{
"inLabel":16040,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16040,
"distance":150,
"installed":true,
"nexthop":"10.0.7.4"
}
]
},
"16050":{
"inLabel":16050,
"installed":true,
"nexthops":[
{
"type":"SR (IS-IS)",
"outLabel":16050,
"distance":150,
"installed":true,
"nexthop":"10.0.8.5"
}
]
}
}

View File

@ -0,0 +1,13 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "1.1.1.1",
"is-operational": false
}
]
}
}
}

View File

@ -0,0 +1,19 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "1.1.1.1",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,19 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "1.1.1.1",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,23 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "1.1.1.1",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"is-best-candidate-path": false
},
{
"preference": 200,
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,20 @@
{
"6666":{
"inLabel":6666,
"installed":true,
"nexthops":[
{
"type":"SR-TE",
"outLabel":16050,
"outLabelStack":[
16050,
16030,
16010
],
"distance":150,
"installed":true,
"nexthop":"10.0.8.5"
}
]
}
}

View File

@ -0,0 +1,27 @@
log file zebra.log
!
hostname rt6
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 6.6.6.6/32
ipv6 address 2001:db8:1000::6/128
!
interface eth-rt4
ip address 10.0.7.6/24
!
interface eth-rt5
ip address 10.0.8.6/24
!
interface eth-dst
ip address 10.0.11.1/24
!
ip forwarding
!
ip route 9.9.9.2/32 10.0.11.2
!
line vty
!

View File

@ -0,0 +1,525 @@
#!/usr/bin/env python
#
# test_isis_sr_topo1.py
# Part of NetDEF Topology Tests
#
# Copyright (c) 2019 by
# Network Device Education Foundation, Inc. ("NetDEF")
#
# Permission to use, copy, modify, and/or distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice appear
# in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
# OF THIS SOFTWARE.
#
"""
test_isis_sr_te_topo1.py:
+---------+
| |
| RT1 |
| 1.1.1.1 |
| |
+---------+
|eth-sw1
|
|
|
+---------+ | +---------+
| | | | |
| RT2 |eth-sw1 | eth-sw1| RT3 |
| 2.2.2.2 +----------+----------+ 3.3.3.3 |
| | 10.0.1.0/24 | |
+---------+ +---------+
eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2
| | | |
10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24
| | | |
eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2
+---------+ +---------+
| | | |
| RT4 | 10.0.6.0/24 | RT5 |
| 4.4.4.4 +---------------------+ 5.5.5.5 |
| |eth-rt5 eth-rt4| |
+---------+ +---------+
eth-rt6| |eth-rt6
| |
10.0.7.0/24| |10.0.8.0/24
| +---------+ |
| | | |
| | RT6 | |
+----------+ 6.6.6.6 +-----------+
eth-rt4| |eth-rt5
+---------+
|eth-dst (.1)
|
|10.0.11.0/24
|
|eth-rt6 (.2)
+---------+
| |
| DST |
| 9.9.9.2 |
| |
+---------+
"""
import os
import sys
import pytest
import json
import re
from time import sleep
from functools import partial
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, '../'))
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
class TemplateTopo(Topo):
"Test topology builder"
def build(self, *_args, **_opts):
"Build function"
tgen = get_topogen(self)
#
# Define FRR Routers
#
for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'dst']:
tgen.add_router(router)
#
# Define connections
#
switch = tgen.add_switch('s1')
switch.add_link(tgen.gears['rt1'], nodeif="eth-sw1")
switch.add_link(tgen.gears['rt2'], nodeif="eth-sw1")
switch.add_link(tgen.gears['rt3'], nodeif="eth-sw1")
switch = tgen.add_switch('s2')
switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-1")
switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-1")
switch = tgen.add_switch('s3')
switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-2")
switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-2")
switch = tgen.add_switch('s4')
switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-1")
switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-1")
switch = tgen.add_switch('s5')
switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-2")
switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-2")
switch = tgen.add_switch('s6')
switch.add_link(tgen.gears['rt4'], nodeif="eth-rt5")
switch.add_link(tgen.gears['rt5'], nodeif="eth-rt4")
switch = tgen.add_switch('s7')
switch.add_link(tgen.gears['rt4'], nodeif="eth-rt6")
switch.add_link(tgen.gears['rt6'], nodeif="eth-rt4")
switch = tgen.add_switch('s8')
switch.add_link(tgen.gears['rt5'], nodeif="eth-rt6")
switch.add_link(tgen.gears['rt6'], nodeif="eth-rt5")
switch = tgen.add_switch('s9')
switch.add_link(tgen.gears['rt6'], nodeif="eth-dst")
switch.add_link(tgen.gears['dst'], nodeif="eth-rt6")
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(TemplateTopo, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
# For all registered routers, load the zebra configuration file
for rname, router in router_list.iteritems():
router.load_config(
TopoRouter.RD_ZEBRA,
os.path.join(CWD, '{}/zebra.conf'.format(rname))
)
router.load_config(
TopoRouter.RD_ISIS,
os.path.join(CWD, '{}/isisd.conf'.format(rname))
)
router.load_config(
TopoRouter.RD_PATH,
os.path.join(CWD, '{}/pathd.conf'.format(rname))
)
router.load_config(
TopoRouter.RD_BGP,
os.path.join(CWD, '{}/bgpd.conf'.format(rname))
)
tgen.start_router()
def teardown_module(mod):
"Teardown the pytest environment"
tgen = get_topogen()
# This function tears down the whole topology.
tgen.stop_topology()
def setup_testcase(msg):
logger.info(msg)
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
return tgen
def print_cmd_result(rname, command):
print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
def compare_json_test(router, command, reference, exact):
output = router.vtysh_cmd(command, isjson=True)
result = topotest.json_cmp(output, reference)
# Note: topotest.json_cmp() just checks on inclusion of keys.
# For exact matching also compare the other way around.
if not result and exact:
return topotest.json_cmp(reference, output)
else:
return result
def cmp_json_output(rname, command, reference, exact=False):
"Compare router JSON output"
logger.info('Comparing router "%s" "%s" output', rname, command)
tgen = get_topogen()
filename = '{}/{}/{}'.format(CWD, rname, reference)
expected = json.loads(open(filename).read())
# Run test function until we get an result. Wait at most 60 seconds.
test_func = partial(compare_json_test,
tgen.gears[rname], command, expected, exact)
_, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
assert diff is None, assertmsg
def cmp_json_output_exact(rname, command, reference):
return cmp_json_output(rname, command, reference, True)
def add_candidate_path(rname, endpoint, pref, name, segment_list='default'):
get_topogen().net[rname].cmd(''' \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "policy color 1 endpoint ''' + endpoint + '''" \
-c "candidate-path preference ''' + str(pref) + ''' name ''' + name + ''' explicit segment-list ''' + segment_list + '''"''')
def delete_candidate_path(rname, endpoint, pref):
get_topogen().net[rname].cmd(''' \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "policy color 1 endpoint ''' + endpoint + '''" \
-c "no candidate-path preference ''' + str(pref) + '''"''')
def add_segment(rname, name, index, label):
get_topogen().net[rname].cmd(''' \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "segment-list ''' + name + '''" \
-c "index ''' + str(index) + ''' mpls label ''' + str(label) + '''"''')
def delete_segment(rname, name, index):
get_topogen().net[rname].cmd(''' \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "segment-list ''' + name + '''" \
-c "no index ''' + str(index) + '''"''')
def create_sr_policy(rname, endpoint, bsid):
get_topogen().net[rname].cmd(''' \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "policy color 1 endpoint ''' + endpoint + '''" \
-c "name default" \
-c "binding-sid ''' + str(bsid) + '''"''')
def delete_sr_policy(rname, endpoint):
get_topogen().net[rname].cmd(''' \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "no policy color 1 endpoint ''' + endpoint + '''"''')
def create_prefix_sid(rname, prefix, sid):
get_topogen().net[rname].cmd(''' \
vtysh -c "conf t" \
-c "router isis 1" \
-c "segment-routing prefix ''' + prefix + " index " + str(sid) + '''"''')
def delete_prefix_sid(rname, prefix):
get_topogen().net[rname].cmd(''' \
vtysh -c "conf t" \
-c "router isis 1" \
-c "no segment-routing prefix "''' + prefix)
#
# Step 1
#
# Checking the MPLS table using a single SR Policy and a single Candidate Path
#
def test_srte_init_step1():
setup_testcase("Test (step 1): wait for IS-IS convergence / label distribution")
for rname in ['rt1', 'rt6']:
cmp_json_output(rname,
"show mpls table json",
"step1/show_mpls_table_without_candidate.ref")
def test_srte_add_candidate_check_mpls_table_step1():
setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path")
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
add_candidate_path(rname, endpoint, 100, 'default')
cmp_json_output(rname,
"show mpls table json",
"step1/show_mpls_table_with_candidate.ref")
delete_candidate_path(rname, endpoint, 100)
def test_srte_reinstall_sr_policy_check_mpls_table_step1():
setup_testcase("Test (step 1): check MPLS table after the SR Policy was removed and reinstalled")
for rname, endpoint, bsid in [('rt1', '6.6.6.6', 1111), ('rt6', '1.1.1.1', 6666)]:
add_candidate_path(rname, endpoint, 100, 'default')
delete_sr_policy(rname, endpoint)
cmp_json_output(rname,
"show mpls table json",
"step1/show_mpls_table_without_candidate.ref")
create_sr_policy(rname, endpoint, bsid)
add_candidate_path(rname, endpoint, 100, 'default')
cmp_json_output(rname,
"show mpls table json",
"step1/show_mpls_table_with_candidate.ref")
delete_candidate_path(rname, endpoint, 100)
#
# Step 2
#
# Checking pathd operational data using a single SR Policy and a single Candidate Path
#
def test_srte_bare_policy_step2():
setup_testcase("Test (step 2): bare SR Policy should not be operational")
for rname in ['rt1', 'rt6']:
cmp_json_output_exact(rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step2/show_operational_data.ref")
def test_srte_add_candidate_check_operational_data_step2():
setup_testcase("Test (step 2): add single Candidate Path, SR Policy should be operational")
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
add_candidate_path(rname, endpoint, 100, 'default')
cmp_json_output(rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step2/show_operational_data_with_candidate.ref")
def test_srte_config_remove_candidate_check_operational_data_step2():
setup_testcase("Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore")
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
delete_candidate_path(rname, endpoint, 100)
cmp_json_output_exact(rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step2/show_operational_data.ref")
#
# Step 3
#
# Testing the Candidate Path selection
#
def test_srte_add_two_candidates_step3():
setup_testcase("Test (step 3): second Candidate Path has higher Priority")
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
for pref, cand_name in [('100', 'first'), ('200', 'second')]:
add_candidate_path(rname, endpoint, pref, cand_name)
cmp_json_output(rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step3/show_operational_data_with_two_candidates.ref")
# cleanup
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
for pref in ['100', '200']:
delete_candidate_path(rname, endpoint, pref)
def test_srte_add_two_candidates_with_reverse_priority_step3():
setup_testcase("Test (step 3): second Candidate Path has lower Priority")
# Use reversed priorities here
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
for pref, cand_name in [('200', 'first'), ('100', 'second')]:
add_candidate_path(rname, endpoint, pref, cand_name)
cmp_json_output(rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step3/show_operational_data_with_two_candidates.ref")
# cleanup
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
for pref in ['100', '200']:
delete_candidate_path(rname, endpoint, pref)
def test_srte_remove_best_candidate_step3():
setup_testcase("Test (step 3): delete the Candidate Path with higher priority")
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
for pref, cand_name in [('100', 'first'), ('200', 'second')]:
add_candidate_path(rname, endpoint, pref, cand_name)
# Delete candidate with higher priority
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
delete_candidate_path(rname, endpoint, 200)
# Candidate with lower priority should get active now
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
cmp_json_output(rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step3/show_operational_data_with_single_candidate.ref")
# cleanup
delete_candidate_path(rname, endpoint, 100)
#
# Step 4
#
# Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications
#
def test_srte_change_segment_list_check_mpls_table_step4():
setup_testcase("Test (step 4): check MPLS table for changed Segment List")
for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]:
add_candidate_path(rname, endpoint, 100, 'default')
# now change the segment list name
add_candidate_path(rname, endpoint, 100, 'default', 'test')
cmp_json_output(rname,
"show mpls table json",
"step4/show_mpls_table.ref")
delete_candidate_path(rname, endpoint, 100)
def test_srte_segment_list_add_segment_check_mpls_table_step4():
setup_testcase("Test (step 4): check MPLS table for added (then changed and finally deleted) segment")
add_candidate_path('rt1', '6.6.6.6', 100, 'default', 'test')
# first add a new segment
add_segment('rt1', 'test', 25, 16050)
cmp_json_output('rt1',
"show mpls table json",
"step4/show_mpls_table_add_segment.ref")
# ... then change it ...
add_segment('rt1', 'test', 25, 16030)
cmp_json_output('rt1',
"show mpls table json",
"step4/show_mpls_table_change_segment.ref")
# ... and finally delete it
delete_segment('rt1', 'test', 25)
cmp_json_output('rt1',
"show mpls table json",
"step4/show_mpls_table.ref")
delete_candidate_path('rt1', '6.6.6.6', 100)
#
# Step 5
#
# Checking the nexthop using a single SR Policy and a Candidate Path with configured route-map
#
def test_srte_route_map_with_sr_policy_check_nextop_step5():
setup_testcase("Test (step 5): recursive nexthop learned through BGP neighbour should be aligned with SR Policy from route-map")
# (re-)build the SR Policy two times to ensure that reinstalling still works
for i in [1,2]:
cmp_json_output('rt1',
"show ip route bgp json",
"step5/show_ip_route_bgp_inactive_srte.ref")
delete_sr_policy('rt1', '6.6.6.6')
cmp_json_output('rt1',
"show ip route bgp json",
"step5/show_ip_route_bgp_inactive_srte.ref")
create_sr_policy('rt1', '6.6.6.6', 1111)
cmp_json_output('rt1',
"show ip route bgp json",
"step5/show_ip_route_bgp_inactive_srte.ref")
add_candidate_path('rt1', '6.6.6.6', 100, 'default')
cmp_json_output('rt1',
"show ip route bgp json",
"step5/show_ip_route_bgp_active_srte.ref")
delete_candidate_path('rt1', '6.6.6.6', 100)
def test_srte_route_map_with_sr_policy_reinstall_prefix_sid_check_nextop_step5():
setup_testcase("Test (step 5): remove and re-install prefix SID on fist path element and check SR Policy activity")
# first add a candidate path so the SR Policy is active
add_candidate_path('rt1', '6.6.6.6', 100, 'default')
cmp_json_output('rt1',
"show yang operational-data /frr-pathd:pathd pathd",
"step5/show_operational_data_active.ref")
# delete prefix SID from first element of the configured path and check
# if the SR Policy is inactive since the label can't be resolved anymore
delete_prefix_sid('rt5', "5.5.5.5/32")
cmp_json_output('rt1',
"show yang operational-data /frr-pathd:pathd pathd",
"step5/show_operational_data_inactive.ref")
cmp_json_output('rt1',
"show ip route bgp json",
"step5/show_ip_route_bgp_inactive_srte.ref")
# re-create the prefix SID and check if the SR Policy is active
create_prefix_sid('rt5', "5.5.5.5/32", 50)
cmp_json_output('rt1',
"show yang operational-data /frr-pathd:pathd pathd",
"step5/show_operational_data_active.ref")
cmp_json_output('rt1',
"show ip route bgp json",
"step5/show_ip_route_bgp_active_srte.ref")
# Memory leak test template
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
if not tgen.is_memleak_enabled():
pytest.skip('Memory leak test/report is disabled')
tgen.report_memory_leaks()
if __name__ == '__main__':
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View File

@ -552,6 +552,7 @@ class TopoRouter(TopoGear):
RD_SHARP = 14
RD_BABEL = 15
RD_PBRD = 16
RD_PATH = 17
RD = {
RD_ZEBRA: "zebra",
RD_RIP: "ripd",
@ -569,6 +570,7 @@ class TopoRouter(TopoGear):
RD_SHARP: "sharpd",
RD_BABEL: "babeld",
RD_PBRD: "pbrd",
RD_PATH: 'pathd',
}
def __init__(self, tgen, cls, name, **params):

View File

@ -1096,6 +1096,7 @@ class Router(Node):
"sharpd": 0,
"babeld": 0,
"pbrd": 0,
'pathd': 0
}
self.daemons_options = {"zebra": ""}
self.reportCores = True

View File

@ -626,6 +626,22 @@ end
ctx_keys = []
current_context_lines = []
elif (
line == "exit"
and len(ctx_keys) > 1
and ctx_keys[0].startswith("segment-routing")
):
self.save_contexts(ctx_keys, current_context_lines)
# Start a new context
ctx_keys = ctx_keys[:-1]
current_context_lines = []
log.debug(
"LINE %-50s: popping segment routing sub-context to ctx%-50s",
line,
ctx_keys
)
elif line in ["exit-address-family", "exit", "exit-vnc"]:
# if this exit is for address-family ipv4 unicast, ignore the pop
if main_ctx_key:
@ -637,7 +653,7 @@ end
log.debug(
"LINE %-50s: popping from subcontext to ctx%-50s",
line,
ctx_keys,
ctx_keys
)
elif line in ["exit-vni", "exit-ldp-if"]:
@ -665,6 +681,27 @@ end
current_context_lines = []
new_ctx = False
log.debug("LINE %-50s: entering new context, %-50s", line, ctx_keys)
elif (
line.startswith("peer ")
and len(ctx_keys) == 4
and ctx_keys[0].startswith("segment-routing")
and ctx_keys[1].startswith("traffic-eng")
and ctx_keys[2].startswith("pcep")
and ctx_keys[3].startswith("pcc")
):
# If there is no precedence, we add the default one (255) so
# the line is not removed and added back
m = re.search('peer ([^ ]*)', line)
if (m != None):
(name,) = m.groups()
line = "peer %s precedence 255" % (name,)
current_context_lines.append(line)
log.debug(
"LINE %-50s: append to current_context_lines, %-50s", line, ctx_keys
)
elif (
line.startswith("address-family ")
or line.startswith("vnc defaults")
@ -727,6 +764,135 @@ end
)
ctx_keys.append(line)
elif (
line.startswith("traffic-eng")
and len(ctx_keys) == 1
and ctx_keys[0].startswith("segment-routing")
):
# Save old context first
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
log.debug(
"LINE %-50s: entering segment routing sub-context, append to ctx_keys", line
)
ctx_keys.append(line)
elif (
line.startswith("segment-list ")
and len(ctx_keys) == 2
and ctx_keys[0].startswith("segment-routing")
and ctx_keys[1].startswith("traffic-eng")
):
# Save old context first
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
log.debug(
"LINE %-50s: entering segment routing sub-context, append to ctx_keys", line
)
ctx_keys.append(line)
elif (
line.startswith("policy ")
and len(ctx_keys) == 2
and ctx_keys[0].startswith("segment-routing")
and ctx_keys[1].startswith("traffic-eng")
):
# Save old context first
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
log.debug(
"LINE %-50s: entering segment routing sub-context, append to ctx_keys", line
)
ctx_keys.append(line)
elif (
line.startswith("candidate-path ")
and line.endswith(" dynamic")
and len(ctx_keys) == 3
and ctx_keys[0].startswith("segment-routing")
and ctx_keys[1].startswith("traffic-eng")
and ctx_keys[2].startswith("policy")
):
# Save old context first
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
main_ctx_key = copy.deepcopy(ctx_keys)
log.debug(
"LINE %-50s: entering candidate-path sub-context, append to ctx_keys", line
)
ctx_keys.append(line)
elif (
line.startswith("pcep")
and len(ctx_keys) == 2
and ctx_keys[0].startswith("segment-routing")
and ctx_keys[1].startswith("traffic-eng")
):
# Save old context first
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
main_ctx_key = copy.deepcopy(ctx_keys)
log.debug(
"LINE %-50s: entering pcep sub-context, append to ctx_keys", line
)
ctx_keys.append(line)
elif (
line.startswith("pce-config ")
and len(ctx_keys) == 3
and ctx_keys[0].startswith("segment-routing")
and ctx_keys[1].startswith("traffic-eng")
and ctx_keys[2].startswith("pcep")
):
# Save old context first
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
main_ctx_key = copy.deepcopy(ctx_keys)
log.debug(
"LINE %-50s: entering pce-config sub-context, append to ctx_keys", line
)
ctx_keys.append(line)
elif (
line.startswith("pce ")
and len(ctx_keys) == 3
and ctx_keys[0].startswith("segment-routing")
and ctx_keys[1].startswith("traffic-eng")
and ctx_keys[2].startswith("pcep")
):
# Save old context first
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
main_ctx_key = copy.deepcopy(ctx_keys)
log.debug(
"LINE %-50s: entering pce sub-context, append to ctx_keys", line
)
ctx_keys.append(line)
elif (
line.startswith("pcc")
and len(ctx_keys) == 3
and ctx_keys[0].startswith("segment-routing")
and ctx_keys[1].startswith("traffic-eng")
and ctx_keys[2].startswith("pcep")
):
# Save old context first
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
main_ctx_key = copy.deepcopy(ctx_keys)
log.debug(
"LINE %-50s: entering pcc sub-context, append to ctx_keys", line
)
ctx_keys.append(line)
else:
# Continuing in an existing context, add non-commented lines to it
current_context_lines.append(line)
@ -1244,6 +1410,9 @@ def compare_context_objects(newconf, running):
# Compare the two Config objects to find the lines that we need to add/del
lines_to_add = []
lines_to_del = []
pollist_to_del = []
seglist_to_del = []
candidates_to_add = []
delete_bgpd = False
# Find contexts that are in newconf but not in running
@ -1326,6 +1495,40 @@ def compare_context_objects(newconf, running):
(running_ctx_keys[:1], None) in lines_to_del):
continue
# Segment routing and traffic engineering never need to be deleted
elif (
len(running_ctx_keys) > 1
and len(running_ctx_keys) < 3
and running_ctx_keys[0].startswith('segment-routing')
):
continue
# Neither the pcep command
elif (
len(running_ctx_keys) == 3
and running_ctx_keys[0].startswith('segment-routing')
and running_ctx_keys[2].startswith('pcep4')
):
continue
# Segment lists can only be deleted after we removed all the candidate paths that
# use them, so add them to a separate array that is going to be appended at the end
elif (
len(running_ctx_keys) == 3
and running_ctx_keys[0].startswith('segment-routing')
and running_ctx_keys[2].startswith('segment-list')
):
seglist_to_del.append((running_ctx_keys, None))
# Policies must be deleted after there candidate path, to be sure
# we add them to a separate array that is going to be appended at the end
elif (
len(running_ctx_keys) == 3
and running_ctx_keys[0].startswith('segment-routing')
and running_ctx_keys[2].startswith('policy')
):
pollist_to_del.append((running_ctx_keys, None))
# Non-global context
elif running_ctx_keys and not any(
"address-family" in key for key in running_ctx_keys
@ -1340,6 +1543,14 @@ def compare_context_objects(newconf, running):
for line in running_ctx.lines:
lines_to_del.append((running_ctx_keys, line))
# if we have some policies commands to delete, append them to lines_to_del
if len(pollist_to_del) > 0:
lines_to_del.extend(pollist_to_del)
# if we have some segment list commands to delete, append them to lines_to_del
if len(seglist_to_del) > 0:
lines_to_del.extend(seglist_to_del)
# Find the lines within each context to add
# Find the lines within each context to del
for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts):
@ -1349,7 +1560,19 @@ def compare_context_objects(newconf, running):
for line in newconf_ctx.lines:
if line not in running_ctx.dlines:
lines_to_add.append((newconf_ctx_keys, line))
# candidate paths can only be added after the policy and segment list,
# so add them to a separate array that is going to be appended at the end
if (
len(newconf_ctx_keys) == 3
and newconf_ctx_keys[0].startswith('segment-routing')
and newconf_ctx_keys[2].startswith('policy ')
and line.startswith('candidate-path ')
):
candidates_to_add.append((newconf_ctx_keys, line))
else:
lines_to_add.append((newconf_ctx_keys, line))
for line in running_ctx.lines:
if line not in newconf_ctx.dlines:
@ -1358,10 +1581,27 @@ def compare_context_objects(newconf, running):
for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts):
if newconf_ctx_keys not in running.contexts:
lines_to_add.append((newconf_ctx_keys, None))
for line in newconf_ctx.lines:
lines_to_add.append((newconf_ctx_keys, line))
# candidate paths can only be added after the policy and segment list,
# so add them to a separate array that is going to be appended at the end
if (
len(newconf_ctx_keys) == 4
and newconf_ctx_keys[0].startswith('segment-routing')
and newconf_ctx_keys[3].startswith('candidate-path')
):
candidates_to_add.append((newconf_ctx_keys, None))
for line in newconf_ctx.lines:
candidates_to_add.append((newconf_ctx_keys, line))
else:
lines_to_add.append((newconf_ctx_keys, None))
for line in newconf_ctx.lines:
lines_to_add.append((newconf_ctx_keys, line))
# if we have some candidate paths commands to add, append them to lines_to_add
if len(candidates_to_add) > 0:
lines_to_add.extend(candidates_to_add)
(lines_to_add, lines_to_del) = check_for_exit_vrf(lines_to_add, lines_to_del)
(lines_to_add, lines_to_del) = ignore_delete_re_add_lines(
@ -1523,10 +1763,11 @@ if __name__ == "__main__":
"staticd",
"vrrpd",
"ldpd",
"pathd",
]:
log.error(
"Daemon %s is not a valid option for 'show running-config'" % args.daemon
)
msg = "Daemon %s is not a valid option for 'show running-config'" % args.daemon
print(msg)
log.error(msg)
sys.exit(1)
vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket, args.pathspace)
@ -1574,6 +1815,8 @@ if __name__ == "__main__":
else:
running.load_from_show_running(args.daemon)
(lines_to_add, lines_to_del) = compare_context_objects(newconf, running)
lines_to_configure = []

View File

@ -27,7 +27,7 @@ FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
# Local Daemon selection may be done by using /etc/frr/daemons.
# See /usr/share/doc/frr/README.Debian.gz for further information.
# Keep zebra first and do not list watchfrr!
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd"
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"
MAX_INSTANCES=5
RELOAD_SCRIPT="$D_PATH/frr-reload.py"

View File

@ -139,6 +139,7 @@ struct vtysh_client vtysh_client[] = {
{.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL},
{.fd = -1, .name = "bfdd", .flag = VTYSH_BFDD, .next = NULL},
{.fd = -1, .name = "vrrpd", .flag = VTYSH_VRRPD, .next = NULL},
{.fd = -1, .name = "pathd", .flag = VTYSH_PATHD, .next = NULL},
};
/* Searches for client by name, returns index */
@ -538,6 +539,15 @@ static int vtysh_execute_func(const char *line, int pager)
|| saved_node == LDP_IPV6_IFACE_NODE)
&& (tried == 1)) {
vtysh_execute("exit");
} else if ((saved_node == SR_SEGMENT_LIST_NODE
|| saved_node == SR_POLICY_NODE
|| saved_node == SR_CANDIDATE_DYN_NODE
|| saved_node == PCEP_NODE
|| saved_node == PCEP_PCE_CONFIG_NODE
|| saved_node == PCEP_PCE_NODE
|| saved_node == PCEP_PCC_NODE)
&& (tried > 0)) {
vtysh_execute("exit");
} else if (tried) {
vtysh_execute("end");
vtysh_execute("configure");
@ -689,6 +699,7 @@ int vtysh_mark_file(const char *filename)
int ret;
vector vline;
int tried = 0;
bool ending;
const struct cmd_element *cmd;
int saved_ret, prev_node;
int lineno = 0;
@ -740,6 +751,12 @@ int vtysh_mark_file(const char *filename)
vty->node = LDP_L2VPN_NODE;
}
break;
case SR_CANDIDATE_DYN_NODE:
if (strncmp(vty_buf_copy, " ", 2)) {
vty_out(vty, " exit\n");
vty->node = SR_POLICY_NODE;
}
break;
default:
break;
}
@ -812,6 +829,31 @@ int vtysh_mark_file(const char *filename)
} else if ((prev_node == BFD_PEER_NODE)
&& (tried == 1)) {
vty_out(vty, "exit\n");
} else if (((prev_node == SEGMENT_ROUTING_NODE)
|| (prev_node == SR_TRAFFIC_ENG_NODE)
|| (prev_node == SR_SEGMENT_LIST_NODE)
|| (prev_node == SR_POLICY_NODE)
|| (prev_node == SR_CANDIDATE_DYN_NODE)
|| (prev_node == PCEP_NODE)
|| (prev_node == PCEP_PCE_CONFIG_NODE)
|| (prev_node == PCEP_PCE_NODE)
|| (prev_node == PCEP_PCC_NODE))
&& (tried > 0)) {
ending = (vty->node != SEGMENT_ROUTING_NODE)
&& (vty->node != SR_TRAFFIC_ENG_NODE)
&& (vty->node != SR_SEGMENT_LIST_NODE)
&& (vty->node != SR_POLICY_NODE)
&& (vty->node != SR_CANDIDATE_DYN_NODE)
&& (vty->node != PCEP_NODE)
&& (vty->node != PCEP_PCE_CONFIG_NODE)
&& (vty->node != PCEP_PCE_NODE)
&& (vty->node != PCEP_PCC_NODE);
if (ending)
tried--;
while (tried-- > 0)
vty_out(vty, "exit\n");
if (ending)
vty_out(vty, "end\n");
} else if (tried) {
vty_out(vty, "end\n");
}
@ -1219,6 +1261,69 @@ static struct cmd_node pw_node = {
.prompt = "%s(config-pw)# ",
};
static struct cmd_node segment_routing_node = {
.name = "segment-routing",
.node = SEGMENT_ROUTING_NODE,
.parent_node = CONFIG_NODE,
.prompt = "%s(config-sr)# ",
};
static struct cmd_node sr_traffic_eng_node = {
.name = "sr traffic-eng",
.node = SR_TRAFFIC_ENG_NODE,
.parent_node = SEGMENT_ROUTING_NODE,
.prompt = "%s(config-sr-te)# ",
};
static struct cmd_node srte_segment_list_node = {
.name = "srte segment-list",
.node = SR_SEGMENT_LIST_NODE,
.parent_node = SR_TRAFFIC_ENG_NODE,
.prompt = "%s(config-sr-te-segment-list)# ",
};
static struct cmd_node srte_policy_node = {
.name = "srte policy",
.node = SR_POLICY_NODE,
.parent_node = SR_TRAFFIC_ENG_NODE,
.prompt = "%s(config-sr-te-policy)# ",
};
static struct cmd_node srte_candidate_dyn_node = {
.name = "srte candidate-dyn",
.node = SR_CANDIDATE_DYN_NODE,
.parent_node = SR_POLICY_NODE,
.prompt = "%s(config-sr-te-candidate)# ",
};
static struct cmd_node pcep_node = {
.name = "srte pcep",
.node = PCEP_NODE,
.parent_node = SR_TRAFFIC_ENG_NODE,
.prompt = "%s(config-sr-te-pcep)# "
};
static struct cmd_node pcep_pcc_node = {
.name = "srte pcep pcc",
.node = PCEP_PCC_NODE,
.parent_node = PCEP_NODE,
.prompt = "%s(config-sr-te-pcep-pcc)# ",
};
static struct cmd_node pcep_pce_node = {
.name = "srte pcep pce-peer",
.node = PCEP_PCE_NODE,
.parent_node = PCEP_NODE,
.prompt = "%s(config-sr-te-pcep-pce-peer)# ",
};
static struct cmd_node pcep_pce_config_node = {
.name = "srte pcep pce-config",
.node = PCEP_PCE_CONFIG_NODE,
.parent_node = PCEP_NODE,
.prompt = "%s(pcep-sr-te-pcep-pce-config)# ",
};
static struct cmd_node vrf_node = {
.name = "vrf",
.node = VRF_NODE,
@ -1974,6 +2079,102 @@ DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfab
}
#endif /* HAVE_FABRICD */
#if defined(HAVE_PATHD)
DEFUNSH(VTYSH_PATHD, segment_routing, segment_routing_cmd,
"segment-routing",
"Configure segment routing\n")
{
vty->node = SEGMENT_ROUTING_NODE;
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_PATHD, sr_traffic_eng, sr_traffic_eng_cmd,
"traffic-eng",
"Configure SR traffic engineering\n")
{
vty->node = SR_TRAFFIC_ENG_NODE;
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_PATHD, srte_segment_list, srte_segment_list_cmd,
"segment-list WORD$name",
"Segment List\n"
"Segment List Name\n")
{
vty->node = SR_SEGMENT_LIST_NODE;
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_PATHD, srte_policy, srte_policy_cmd,
"policy color (0-4294967295) endpoint <A.B.C.D|X:X::X:X>",
"Segment Routing Policy\n"
"SR Policy color\n"
"SR Policy color value\n"
"SR Policy endpoint\n"
"SR Policy endpoint IPv4 address\n"
"SR Policy endpoint IPv6 address\n")
{
vty->node = SR_POLICY_NODE;
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_PATHD, srte_policy_candidate_dyn_path,
srte_policy_candidate_dyn_path_cmd,
"candidate-path preference (0-4294967295) name WORD dynamic",
"Segment Routing Policy Candidate Path\n"
"Segment Routing Policy Candidate Path Preference\n"
"Administrative Preference\n"
"Segment Routing Policy Candidate Path Name\n"
"Symbolic Name\n"
"Dynamic Path\n")
{
vty->node = SR_CANDIDATE_DYN_NODE;
return CMD_SUCCESS;
}
#if defined(HAVE_PATHD_PCEP)
DEFUNSH(VTYSH_PATHD, pcep, pcep_cmd,
"pcep",
"Configure SR pcep\n")
{
vty->node = PCEP_NODE;
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_PATHD, pcep_cli_pcc, pcep_cli_pcc_cmd,
"[no] pcc",
NO_STR
"PCC configuration\n")
{
vty->node = PCEP_PCC_NODE;
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_PATHD, pcep_cli_pce, pcep_cli_pce_cmd,
"[no] pce WORD",
NO_STR
"PCE configuration\n"
"Peer name\n")
{
vty->node = PCEP_PCE_NODE;
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_PATHD, pcep_cli_pcep_pce_config, pcep_cli_pcep_pce_config_cmd,
"[no] pce-config WORD",
NO_STR
"PCEP peer Configuration Group\n"
"PCEP peer Configuration Group name\n")
{
vty->node = PCEP_PCE_CONFIG_NODE;
return CMD_SUCCESS;
}
#endif /* HAVE_PATHD_PCEP */
#endif /* HAVE_PATHD */
DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd,
"route-map WORD <deny|permit> (1-65535)",
"Create route-map or enter route-map command mode\n"
@ -2347,6 +2548,18 @@ DEFUNSH(VTYSH_KEYS, vtysh_quit_keys, vtysh_quit_keys_cmd, "quit",
return vtysh_exit_keys(self, vty, argc, argv);
}
DEFUNSH(VTYSH_PATHD, vtysh_exit_pathd, vtysh_exit_pathd_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
return vtysh_exit(vty);
}
DEFUNSH(VTYSH_PATHD, vtysh_quit_pathd, vtysh_quit_pathd_cmd, "quit",
"Exit current mode and down to previous mode\n")
{
return vtysh_exit_pathd(self, vty, argc, argv);
}
DEFUNSH(VTYSH_ALL, vtysh_exit_line_vty, vtysh_exit_line_vty_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
@ -4144,6 +4357,64 @@ void vtysh_init_vty(void)
install_element(BFD_PROFILE_NODE, &vtysh_end_all_cmd);
#endif /* HAVE_BFDD */
#if defined(HAVE_PATHD)
install_node(&segment_routing_node);
install_node(&sr_traffic_eng_node);
install_node(&srte_segment_list_node);
install_node(&srte_policy_node);
install_node(&srte_candidate_dyn_node);
install_element(SEGMENT_ROUTING_NODE, &vtysh_exit_pathd_cmd);
install_element(SEGMENT_ROUTING_NODE, &vtysh_quit_pathd_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_quit_pathd_cmd);
install_element(SR_SEGMENT_LIST_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_SEGMENT_LIST_NODE, &vtysh_quit_pathd_cmd);
install_element(SR_POLICY_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_POLICY_NODE, &vtysh_quit_pathd_cmd);
install_element(SR_CANDIDATE_DYN_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_CANDIDATE_DYN_NODE, &vtysh_quit_pathd_cmd);
install_element(SEGMENT_ROUTING_NODE, &vtysh_end_all_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_end_all_cmd);
install_element(SR_SEGMENT_LIST_NODE, &vtysh_end_all_cmd);
install_element(SR_POLICY_NODE, &vtysh_end_all_cmd);
install_element(SR_CANDIDATE_DYN_NODE, &vtysh_end_all_cmd);
install_element(CONFIG_NODE, &segment_routing_cmd);
install_element(SEGMENT_ROUTING_NODE, &sr_traffic_eng_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &srte_segment_list_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &srte_policy_cmd);
install_element(SR_POLICY_NODE, &srte_policy_candidate_dyn_path_cmd);
#if defined(HAVE_PATHD_PCEP)
install_node(&pcep_node);
install_node(&pcep_pcc_node);
install_node(&pcep_pce_node);
install_node(&pcep_pce_config_node);
install_element(PCEP_NODE, &vtysh_exit_pathd_cmd);
install_element(PCEP_NODE, &vtysh_quit_pathd_cmd);
install_element(PCEP_PCC_NODE, &vtysh_exit_pathd_cmd);
install_element(PCEP_PCC_NODE, &vtysh_quit_pathd_cmd);
install_element(PCEP_PCE_NODE, &vtysh_exit_pathd_cmd);
install_element(PCEP_PCE_NODE, &vtysh_quit_pathd_cmd);
install_element(PCEP_PCE_CONFIG_NODE, &vtysh_exit_pathd_cmd);
install_element(PCEP_PCE_CONFIG_NODE, &vtysh_quit_pathd_cmd);
install_element(PCEP_NODE, &vtysh_end_all_cmd);
install_element(PCEP_PCC_NODE, &vtysh_end_all_cmd);
install_element(PCEP_PCE_NODE, &vtysh_end_all_cmd);
install_element(PCEP_PCE_CONFIG_NODE, &vtysh_end_all_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &pcep_cmd);
install_element(PCEP_NODE, &pcep_cli_pcc_cmd);
install_element(PCEP_NODE, &pcep_cli_pcep_pce_config_cmd);
install_element(PCEP_NODE, &pcep_cli_pce_cmd);
#endif /* HAVE_PATHD_PCEP */
#endif /* HAVE_PATHD */
/* keychain */
install_node(&keychain_node);
install_element(CONFIG_NODE, &key_chain_cmd);

View File

@ -43,6 +43,7 @@ DECLARE_MGROUP(MVTYSH)
#define VTYSH_BFDD 0x10000
#define VTYSH_FABRICD 0x20000
#define VTYSH_VRRPD 0x40000
#define VTYSH_PATHD 0x80000
#define VTYSH_WAS_ACTIVE (-2)
@ -51,7 +52,7 @@ DECLARE_MGROUP(MVTYSH)
/* watchfrr is not in ALL since library CLI functions should not be
* run on it (logging & co. should stay in a fixed/frozen config, and
* things like prefix lists are not even initialised) */
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD|VTYSH_PATHD
#define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD
#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD

480
yang/frr-pathd.yang Normal file
View File

@ -0,0 +1,480 @@
module frr-pathd {
yang-version 1.1;
namespace "http://frrouting.org/yang/pathd";
prefix frr-pathd;
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
import ietf-routing-types {
prefix rt-types;
}
import frr-interface {
prefix frr-interface;
}
organization
"Free Range Routing";
contact
"FRR Users List: <mailto:frog@lists.frrouting.org>
FRR Development List: <mailto:dev@lists.frrouting.org>";
description
"This module defines a model for managing FRR pathd daemon.";
revision 2018-11-06 {
description
"Initial revision.";
}
typedef protocol-origin-type {
description
"Indication for the protocol origin of an object.";
type enumeration {
enum pcep {
value 1;
description "The object was created through PCEP";
}
enum bgp {
value 2;
description "The object was created through GBP";
}
enum local {
value 3;
description "The object was created through CLI, Yang model via Netconf, gRPC, etc";
}
}
}
typedef originator-type {
type string {
length "1..64";
}
description
"Identifier of the originator of an object, could be 'config', '1.1.1.1:4189' or '2001:db8:85a3::8a2e:370:7334:4189'";
}
container pathd {
container srte {
list segment-list {
key "name";
description "Segment-list properties";
leaf name {
type string {
length "1..64";
}
description "Segment-list name";
}
leaf protocol-origin {
type protocol-origin-type;
mandatory true;
description
"Indication for the protocol origin of the segment list.";
}
leaf originator {
type originator-type;
mandatory true;
description "Originator of the segment list";
}
list segment {
key "index";
description "Configure Segment/hop at the index";
leaf index {
type uint32;
description "Segment index";
}
leaf sid-value {
type rt-types:mpls-label;
mandatory true;
description "MPLS label value";
}
container nai {
presence "The segement has a Node or Adjacency Identifier";
leaf type {
description "NAI type";
mandatory true;
type enumeration {
enum ipv4_node {
value 1;
description "IPv4 node identifier";
}
enum ipv6_node {
value 2;
description "IPv6 node identifier";
}
enum ipv4_adjacency {
value 3;
description "IPv4 adjacency";
}
enum ipv6_adjacency {
value 4;
description "IPv6 adjacency";
}
enum ipv4_unnumbered_adjacency {
value 5;
description "IPv4 unnumbered adjacency";
}
}
}
leaf local-address {
type inet:ip-address;
mandatory true;
}
leaf local-interface {
type uint32;
mandatory true;
when "../type = 'ipv4_unnumbered_adjacency'";
}
leaf remote-address {
type inet:ip-address;
mandatory true;
when "../type = 'ipv4_adjacency' or ../type = 'ipv6_adjacency' or ../type = 'ipv4_unnumbered_adjacency'";
}
leaf remote-interface {
type uint32;
mandatory true;
when "../type = 'ipv4_unnumbered_adjacency'";
}
}
}
}
list policy {
key "color endpoint";
unique "name";
leaf color {
type uint32;
description
"Color of the SR Policy.";
}
leaf endpoint {
type inet:ip-address;
description
"Indication for the endpoint of the SR Policy.";
}
leaf name {
type string {
length "1..64";
}
description
"Name of the SR Policy.";
}
leaf binding-sid {
type rt-types:mpls-label;
description
"BSID of the SR Policy.";
}
leaf is-operational {
type boolean;
config false;
description
"True if a valid candidate path of this policy is operational in zebra, False otherwise";
}
list candidate-path {
unique "name";
description
"List of Candidate Paths of the SR Policy.";
key "preference";
leaf preference {
type uint32;
description
"Administrative preference.";
}
leaf name {
type string {
length "1..64";
}
mandatory true;
description
"Symbolic Name of the Candidate Path.";
}
leaf is-best-candidate-path {
type boolean;
config false;
description
"True if the candidate path is the best candidate path, False otherwise";
}
leaf protocol-origin {
type protocol-origin-type;
mandatory true;
description
"Indication for the protocol origin of the Candidate Path.";
}
leaf originator {
type originator-type;
mandatory true;
description "Originator of the candidate path";
}
leaf discriminator {
type uint32;
config false;
description "Candidate path distinguisher";
}
leaf type {
description
"Type of the Candidate Path.";
mandatory true;
type enumeration {
enum explicit {
value 1;
}
enum dynamic {
value 2;
}
}
}
leaf segment-list-name {
type leafref {
path ../../../segment-list/name;
}
description
"The name of the Segment List to use as LSP.";
}
container constraints {
when "../type = 'dynamic'";
description
"Generic dynamic path constraints";
container bandwidth {
presence "If the candidate has a bandwidth constraint";
description
"The bandwidth needed by the candidate path.";
leaf required {
type boolean;
default "true";
description
"If the bandwidth limitation is a requirement or only a suggestion";
}
leaf value {
mandatory true;
type decimal64 {
fraction-digits 6;
}
}
}
container affinity {
description
"Affinity let you configure how the links should be used when calculating a path.";
leaf exclude-any {
type uint32;
description
"A 32-bit vector representing a set of attribute filters which renders a link unacceptable.";
}
leaf include-any {
type uint32;
description
"A 32-bit vector representing a set of attribute filters which renders a link acceptable. A null set (all bits set to zero) automatically passes.";
}
leaf include-all {
type uint32;
description
"A 32-bit vector representing a set of attribute filters which must be present for a link to be acceptable. A null set (all bits set to zero) automatically passes.";
}
}
list metrics {
key "type";
leaf type {
description
"Type of the metric.";
type enumeration {
enum igp {
value 1;
description "IGP metric";
}
enum te {
value 2;
description "TE metric";
}
enum hc {
value 3;
description "Hop Counts";
}
enum abc {
value 4;
description "Aggregate bandwidth consumption";
}
enum lmll {
value 5;
description "Load of the most loaded link";
}
enum cigp {
value 6;
description "Cumulative IGP cost";
}
enum cte {
value 7;
description "Cumulative TE cost";
}
enum pigp {
value 8;
description "P2MP IGP metric";
}
enum pte {
value 9;
description "P2MP TE metric";
}
enum phc {
value 10;
description "P2MP hop count metric";
}
enum msd {
value 11;
description "Segment-ID (SID) Depth";
}
enum pd {
value 12;
description "Path Delay metric";
}
enum pdv {
value 13;
description "Path Delay Variation metric";
}
enum pl {
value 14;
description "Path Loss metric";
}
enum ppd {
value 15;
description "P2MP Path Delay metric";
}
enum ppdv {
value 16;
description "P2MP Path Delay variation metric";
}
enum ppl {
value 17;
description "P2MP Path Loss metric";
}
enum nap {
value 18;
description "Number of adaptations on a path";
}
enum nlp {
value 19;
description "Number of layers on a path";
}
enum dc {
value 20;
description "Domain Count metric";
}
enum bnc {
value 21;
description "Border Node Count metric";
}
}
}
leaf required {
type boolean;
default "true";
description
"If the metric is a requirement, or if it is only a suggestion";
}
leaf is-bound {
type boolean;
description
"Defines if the value is a bound (a maximum) for the path metric that must not be exceeded.";
}
leaf is-computed {
type boolean;
description
"Defines if the value has been generated by the originator of the path.";
}
leaf value {
mandatory true;
type decimal64 {
fraction-digits 6;
}
}
}
container objective-function {
presence "If the candidate has an objective function constraint";
description
"Define objective function constraint as a list of prefered functions";
leaf required {
type boolean;
default "true";
description
"If an objective function is a requirement, or if it is only a suggestion";
}
leaf type {
description
"Type of objective function.";
mandatory true;
type enumeration {
enum mcp {
value 1;
description "Minimum Cost Path";
}
enum mlp {
value 2;
description "Minimum Load Path";
}
enum mbp {
value 3;
description "Maximum residual Bandwidth Path";
}
enum mbc {
value 4;
description "Minimize aggregate Bandwidth Consumption";
}
enum mll {
value 5;
description "Minimize the Load of the most loaded Link";
}
enum mcc {
value 6;
description "Minimize the Cumulative Cost of a set of paths";
}
enum spt {
value 7;
description "Shortest Path Tree";
}
enum mct {
value 8;
description "Minimum Cost Tree";
}
enum mplp {
value 9;
description "Minimum Packet Loss Path";
}
enum mup {
value 10;
description "Maximum Under-Utilized Path";
}
enum mrup {
value 11;
description "Maximum Reserved Under-Utilized Path";
}
enum mtd {
value 12;
description "Minimize the number of Transit Domains";
}
enum mbn {
value 13;
description "Minimize the number of Border Nodes";
}
enum mctd {
value 14;
description "Minimize the number of Common Transit Domains";
}
enum msl {
value 15;
description "Minimize the number of Shared Links";
}
enum mss {
value 16;
description "Minimize the number of Shared SRLGs";
}
enum msn {
value 17;
description "Minimize the number of Shared Nodes";
}
}
}
}
}
}
}
}
}
}

View File

@ -82,3 +82,7 @@ dist_yangmodels_DATA += yang/frr-bgp-bmp.yang
dist_yangmodels_DATA += yang/frr-bgp-types.yang
dist_yangmodels_DATA += yang/frr-bgp.yang
endif
if PATHD
dist_yangmodels_DATA += yang/frr-pathd.yang
endif