summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rwxr-xr-xconfigure.ac7
-rw-r--r--doc/developer/index.rst1
-rw-r--r--doc/developer/path-internals-daemon.rst115
-rw-r--r--doc/developer/path-internals.rst10
-rw-r--r--doc/developer/path.rst11
-rw-r--r--doc/developer/subdir.am3
-rw-r--r--doc/user/index.rst1
-rw-r--r--doc/user/pathd.rst233
-rw-r--r--doc/user/subdir.am1
-rw-r--r--lib/command.c12
-rw-r--r--lib/command.h5
-rw-r--r--lib/ferr.h2
-rw-r--r--lib/yang.c1
-rw-r--r--pathd/.gitignore2
-rw-r--r--pathd/Makefile10
-rw-r--r--pathd/path_cli.c1108
-rw-r--r--pathd/path_debug.c121
-rw-r--r--pathd/path_debug.h44
-rw-r--r--pathd/path_errors.c43
-rw-r--r--pathd/path_errors.h30
-rw-r--r--pathd/path_main.c154
-rw-r--r--pathd/path_memory.c25
-rw-r--r--pathd/path_memory.h26
-rw-r--r--pathd/path_nb.c351
-rw-r--r--pathd/path_nb.h130
-rw-r--r--pathd/path_nb_config.c731
-rw-r--r--pathd/path_nb_state.c189
-rw-r--r--pathd/path_zebra.c296
-rw-r--r--pathd/path_zebra.h34
-rw-r--r--pathd/pathd.c1127
-rw-r--r--pathd/pathd.conf.sample33
-rw-r--r--pathd/pathd.h412
-rw-r--r--pathd/subdir.am48
-rw-r--r--redhat/frr.spec.in25
-rw-r--r--tests/topotests/isis-sr-te-topo1/dst/zebra.conf19
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf16
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/isisd.conf30
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/pathd.conf21
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref91
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref74
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref13
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref20
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref20
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref25
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref20
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref21
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref21
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref29
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref38
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref20
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref20
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt1/zebra.conf19
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt2/isisd.conf41
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt2/zebra.conf25
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt3/isisd.conf41
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt3/zebra.conf25
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt4/isisd.conf48
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt4/zebra.conf28
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt5/isisd.conf48
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt5/zebra.conf28
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf12
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/isisd.conf36
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/pathd.conf21
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref91
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref74
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref13
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref19
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref19
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref23
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref20
-rw-r--r--tests/topotests/isis-sr-te-topo1/rt6/zebra.conf27
-rwxr-xr-xtests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py525
-rw-r--r--tests/topotests/lib/topogen.py2
-rw-r--r--tests/topotests/lib/topotest.py1
-rwxr-xr-xtools/frr-reload.py163
-rwxr-xr-xtools/frr.in2
-rw-r--r--vtysh/vtysh.c162
-rw-r--r--vtysh/vtysh.h3
-rw-r--r--yang/frr-pathd.yang480
-rw-r--r--yang/subdir.am4
81 files changed, 7828 insertions, 12 deletions
diff --git a/Makefile.am b/Makefile.am
index b6e8a2bce8..90c8407010 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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
diff --git a/configure.ac b/configure.ac
index acedf7573d..168a9041df 100755
--- a/configure.ac
+++ b/configure.ac
@@ -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],
@@ -1675,6 +1677,10 @@ else
esac
fi
+AS_IF([test "$enable_pathd" != "no"], [
+ AC_DEFINE([HAVE_PATHD], [1], [pathd])
+])
+
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
@@ -2489,6 +2495,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"
diff --git a/doc/developer/index.rst b/doc/developer/index.rst
index 5a7da806ff..60a7d505a7 100644
--- a/doc/developer/index.rst
+++ b/doc/developer/index.rst
@@ -18,3 +18,4 @@ FRRouting Developer's Guide
ospf
zebra
vtysh
+ pathd
diff --git a/doc/developer/path-internals-daemon.rst b/doc/developer/path-internals-daemon.rst
new file mode 100644
index 0000000000..29f017284f
--- /dev/null
+++ b/doc/developer/path-internals-daemon.rst
@@ -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.
diff --git a/doc/developer/path-internals.rst b/doc/developer/path-internals.rst
new file mode 100644
index 0000000000..980d359453
--- /dev/null
+++ b/doc/developer/path-internals.rst
@@ -0,0 +1,10 @@
+.. _path_internals:
+
+*********
+Internals
+*********
+
+.. toctree::
+ :maxdepth: 2
+
+ path-internals-daemon
diff --git a/doc/developer/path.rst b/doc/developer/path.rst
new file mode 100644
index 0000000000..b6d2438c58
--- /dev/null
+++ b/doc/developer/path.rst
@@ -0,0 +1,11 @@
+.. _path:
+
+*****
+PATHD
+*****
+
+.. toctree::
+ :maxdepth: 2
+
+ path-internals
+
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index 3c0d203007..400555a2e8 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -46,6 +46,9 @@ 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.rst \
+ doc/developer/path.rst \
doc/developer/rcu.rst \
doc/developer/static-linking.rst \
doc/developer/testing.rst \
diff --git a/doc/user/index.rst b/doc/user/index.rst
index 8ac997f8dd..993acf3b4c 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -50,6 +50,7 @@ Protocols
nhrpd
ospfd
ospf6d
+ pathd
pim
pbr
ripd
diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst
new file mode 100644
index 0000000000..0c825d423b
--- /dev/null
+++ b/doc/user/pathd.rst
@@ -0,0 +1,233 @@
+.. _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.
+
+
+Pathd Configuration
+===================
+
+Example:
+
+.. code-block:: frr
+
+ 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
+ !
+ !
+ !
+
+
+.. _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]
+
+
+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.
+
+
+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.
diff --git a/doc/user/subdir.am b/doc/user/subdir.am
index dd7a193e34..a78d261863 100644
--- a/doc/user/subdir.am
+++ b/doc/user/subdir.am
@@ -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 \
diff --git a/lib/command.c b/lib/command.c
index 87110157f6..eeb14b734d 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -863,6 +863,18 @@ 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;
default:
ret = CONFIG_NODE;
break;
diff --git a/lib/command.h b/lib/command.h
index 1b0504101c..2eae9a1b35 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -145,6 +145,11 @@ 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 */
VTY_NODE, /* Vty node. */
FPM_NODE, /* Dataplane FPM node. */
LINK_PARAMS_NODE, /* Link-parameters node */
diff --git a/lib/ferr.h b/lib/ferr.h
index a89b595e87..4e95431cea 100644
--- a/lib/ferr.h
+++ b/lib/ferr.h
@@ -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
diff --git a/lib/yang.c b/lib/yang.c
index a3e2a395d7..383dc9f5eb 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -92,6 +92,7 @@ static const char *const frr_native_modules[] = {
"frr-isisd",
"frr-vrrpd",
"frr-zebra",
+ "frr-pathd",
};
/* clang-format on */
diff --git a/pathd/.gitignore b/pathd/.gitignore
new file mode 100644
index 0000000000..95f4a99794
--- /dev/null
+++ b/pathd/.gitignore
@@ -0,0 +1,2 @@
+libpath.a
+pathd
diff --git a/pathd/Makefile b/pathd/Makefile
new file mode 100644
index 0000000000..b681a9ab1c
--- /dev/null
+++ b/pathd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. pathd/pathd
+%: ALWAYS
+ @$(MAKE) -s -C .. pathd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/pathd/path_cli.c b/pathd/path_cli.c
new file mode 100644
index 0000000000..a55e6ba406
--- /dev/null
+++ b/pathd/path_cli.c
@@ -0,0 +1,1108 @@
+/*
+ * 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 <float.h>
+#include <math.h>
+#include <zebra.h>
+
+#include "log.h"
+#include "command.h"
+#include "mpls.h"
+#include "northbound_cli.h"
+#include "termtable.h"
+
+#include "pathd/pathd.h"
+#include "pathd/path_nb.h"
+#include "pathd/path_memory.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "pathd/path_cli_clippy.c"
+#endif
+
+#define XPATH_MAXATTRSIZE 64
+#define XPATH_MAXKEYSIZE 42
+#define XPATH_POLICY_BASELEN 100
+#define XPATH_POLICY_MAXLEN (XPATH_POLICY_BASELEN + XPATH_MAXATTRSIZE)
+#define XPATH_CANDIDATE_BASELEN (XPATH_POLICY_BASELEN + XPATH_MAXKEYSIZE)
+#define XPATH_CANDIDATE_MAXLEN (XPATH_CANDIDATE_BASELEN + XPATH_MAXATTRSIZE)
+
+
+static int config_write_segment_routing(struct vty *vty);
+static int config_write_traffic_eng(struct vty *vty);
+static int config_write_segment_lists(struct vty *vty);
+static int config_write_sr_policies(struct vty *vty);
+
+DEFINE_MTYPE_STATIC(PATHD, PATH_CLI, "Client")
+
+/* Vty node structures. */
+static struct cmd_node segment_routing_node = {
+ .name = "segment-routing",
+ .node = SEGMENT_ROUTING_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-sr)# ",
+ .config_write = config_write_segment_routing,
+};
+
+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)# ",
+ .config_write = config_write_traffic_eng,
+};
+
+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)# ",
+ .config_write = config_write_segment_lists,
+};
+
+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)# ",
+ .config_write = config_write_sr_policies,
+};
+
+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)# ",
+};
+
+
+/*
+ * Show SR-TE info
+ */
+DEFPY(show_srte_policy,
+ show_srte_policy_cmd,
+ "show sr-te policy",
+ SHOW_STR
+ "SR-TE info\n"
+ "SR-TE Policy\n")
+{
+ struct ttable *tt;
+ struct srte_policy *policy;
+ char *table;
+
+ if (RB_EMPTY(srte_policy_head, &srte_policies)) {
+ vty_out(vty, "No SR Policies to display.\n\n");
+ return CMD_SUCCESS;
+ }
+
+ /* Prepare table. */
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+ ttable_add_row(tt, "Endpoint|Color|Name|BSID|Status");
+ tt->style.cell.rpad = 2;
+ tt->style.corner = '+';
+ ttable_restyle(tt);
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ char endpoint[46];
+ char binding_sid[16] = "-";
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ if (policy->binding_sid != MPLS_LABEL_NONE)
+ snprintf(binding_sid, sizeof(binding_sid), "%u",
+ policy->binding_sid);
+
+ ttable_add_row(tt, "%s|%u|%s|%s|%s", endpoint, policy->color,
+ policy->name, binding_sid,
+ policy->status == SRTE_POLICY_STATUS_UP
+ ? "Active"
+ : "Inactive");
+ }
+
+ /* Dump the generated table. */
+ table = ttable_dump(tt, "\n");
+ vty_out(vty, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ ttable_del(tt);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Show detailed SR-TE info
+ */
+DEFPY(show_srte_policy_detail,
+ show_srte_policy_detail_cmd,
+ "show sr-te policy detail",
+ SHOW_STR
+ "SR-TE info\n"
+ "SR-TE Policy\n"
+ "Show a detailed summary\n")
+{
+ struct srte_policy *policy;
+
+ if (RB_EMPTY(srte_policy_head, &srte_policies)) {
+ vty_out(vty, "No SR Policies to display.\n\n");
+ return CMD_SUCCESS;
+ }
+
+ vty_out(vty, "\n");
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ struct srte_candidate *candidate;
+ char endpoint[46];
+ char binding_sid[16] = "-";
+ char *segment_list_info;
+ static char undefined_info[] = "(undefined)";
+ static char created_by_pce_info[] = "(created by PCE)";
+
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ if (policy->binding_sid != MPLS_LABEL_NONE)
+ snprintf(binding_sid, sizeof(binding_sid), "%u",
+ policy->binding_sid);
+ vty_out(vty,
+ "Endpoint: %s Color: %u Name: %s BSID: %s Status: %s\n",
+ endpoint, policy->color, policy->name, binding_sid,
+ policy->status == SRTE_POLICY_STATUS_UP ? "Active"
+ : "Inactive");
+
+ RB_FOREACH (candidate, srte_candidate_head,
+ &policy->candidate_paths) {
+ struct srte_segment_list *segment_list;
+
+ segment_list = candidate->lsp->segment_list;
+ if (segment_list == NULL)
+ segment_list_info = undefined_info;
+ else if (segment_list->protocol_origin
+ == SRTE_ORIGIN_PCEP)
+ segment_list_info = created_by_pce_info;
+ else
+ segment_list_info =
+ candidate->lsp->segment_list->name;
+
+ vty_out(vty,
+ " %s Preference: %d Name: %s Type: %s Segment-List: %s Protocol-Origin: %s\n",
+ CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST)
+ ? "*"
+ : " ",
+ candidate->preference, candidate->name,
+ candidate->type == SRTE_CANDIDATE_TYPE_EXPLICIT
+ ? "explicit"
+ : "dynamic",
+ segment_list_info,
+ srte_origin2str(
+ candidate->lsp->protocol_origin));
+ }
+
+ vty_out(vty, "\n");
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY_NOSH(
+ segment_routing_list,
+ segment_routing_cmd,
+ "segment-routing",
+ "Configure segment routing\n")
+{
+ VTY_PUSH_CONTEXT_NULL(SEGMENT_ROUTING_NODE);
+ return CMD_SUCCESS;
+}
+
+DEFPY_NOSH(
+ sr_traffic_eng_list,
+ sr_traffic_eng_cmd,
+ "traffic-eng",
+ "Configure SR traffic engineering\n")
+{
+ VTY_PUSH_CONTEXT_NULL(SR_TRAFFIC_ENG_NODE);
+ return CMD_SUCCESS;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list
+ */
+DEFPY_NOSH(
+ srte_segment_list,
+ srte_segment_list_cmd,
+ "segment-list WORD$name",
+ "Segment List\n"
+ "Segment List Name\n")
+{
+ char xpath[XPATH_MAXLEN];
+ int ret;
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']/protocol-origin",
+ name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "local");
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']/originator", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "config");
+
+ ret = nb_cli_apply_changes(vty, NULL);
+ if (ret == CMD_SUCCESS) {
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']", name);
+ VTY_PUSH_XPATH(SR_SEGMENT_LIST_NODE, xpath);
+ }
+
+ return ret;
+}
+
+DEFPY(srte_no_segment_list,
+ srte_no_segment_list_cmd,
+ "no segment-list WORD$name",
+ NO_STR
+ "Segment List\n"
+ "Segment List Name\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " segment-list %s\n",
+ yang_dnode_get_string(dnode, "./name"));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list/segment
+ */
+DEFPY(srte_segment_list_segment,
+ srte_segment_list_segment_cmd,
+ "index (0-4294967295)$index mpls label (16-1048575)$label "
+ "[nai$has_nai <"
+ "node <A.B.C.D$node_ipv4|X:X::X:X$node_ipv6>"
+ ">]",
+ "Index\n"
+ "Index Value\n"
+ "MPLS or IP Label\n"
+ "Label\n"
+ "Label Value\n"
+ "Segment NAI\n"
+ "NAI node identifier\n"
+ "NAI IPv4 node identifier\n"
+ "NAI IPv6 node identifier\n")
+{
+ char xpath[XPATH_MAXLEN];
+ const char *node_id;
+
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']/sid-value",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str);
+
+ if (has_nai != NULL) {
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai/type",
+ index_str);
+ if (node_ipv4_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv4_node");
+ node_id = node_ipv4_str;
+ } else if (node_ipv6_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv6_node");
+ node_id = node_ipv6_str;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+ snprintf(xpath, sizeof(xpath),
+ "./segment[index='%s']/nai/local-address", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_id);
+ } else {
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_segment_list_no_segment,
+ srte_segment_list_no_segment_cmd,
+ "no index (0-4294967295)$index",
+ NO_STR
+ "Index\n"
+ "Index Value\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_srte_segment_list_segment(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " index %s mpls label %s",
+ yang_dnode_get_string(dnode, "./index"),
+ yang_dnode_get_string(dnode, "./sid-value"));
+ if (yang_dnode_exists(dnode, "./nai")) {
+ struct ipaddr addr;
+ switch (yang_dnode_get_enum(dnode, "./nai/type")) {
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
+ yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
+ vty_out(vty, " nai node %pI4", &addr.ipaddr_v4);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
+ yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
+ vty_out(vty, " nai node %pI6", &addr.ipaddr_v6);
+ break;
+ default:
+ break;
+ }
+ }
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-pathd:pathd/policy
+ */
+DEFPY_NOSH(
+ srte_policy,
+ srte_policy_cmd,
+ "policy color (0-4294967295)$num endpoint <A.B.C.D|X:X::X:X>$endpoint",
+ "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")
+{
+ char xpath[XPATH_POLICY_BASELEN];
+ int ret;
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/policy[color='%s'][endpoint='%s']",
+ num_str, endpoint_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ ret = nb_cli_apply_changes(vty, NULL);
+ if (ret == CMD_SUCCESS)
+ VTY_PUSH_XPATH(SR_POLICY_NODE, xpath);
+
+ return ret;
+}
+
+DEFPY(srte_no_policy,
+ srte_no_policy_cmd,
+ "no policy color (0-4294967295)$num endpoint <A.B.C.D|X:X::X:X>$endpoint",
+ NO_STR
+ "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")
+{
+ char xpath[XPATH_POLICY_BASELEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/policy[color='%s'][endpoint='%s']",
+ num_str, endpoint_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_srte_policy(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " policy color %s endpoint %s\n",
+ yang_dnode_get_string(dnode, "./color"),
+ yang_dnode_get_string(dnode, "./endpoint"));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/name
+ */
+DEFPY(srte_policy_name,
+ srte_policy_name_cmd,
+ "name WORD$name",
+ "Segment Routing Policy name\n"
+ "SR Policy name value\n")
+{
+ nb_cli_enqueue_change(vty, "./name", NB_OP_CREATE, name);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_policy_no_name,
+ srte_policy_no_name_cmd,
+ "no name [WORD]",
+ NO_STR
+ "Segment Routing Policy name\n"
+ "SR Policy name value\n")
+{
+ nb_cli_enqueue_change(vty, "./name", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+void cli_show_srte_policy_name(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " name %s\n", yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/binding-sid
+ */
+DEFPY(srte_policy_binding_sid,
+ srte_policy_binding_sid_cmd,
+ "binding-sid (16-1048575)$label",
+ "Segment Routing Policy Binding-SID\n"
+ "SR Policy Binding-SID label\n")
+{
+ nb_cli_enqueue_change(vty, "./binding-sid", NB_OP_CREATE, label_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_policy_no_binding_sid,
+ srte_policy_no_binding_sid_cmd,
+ "no binding-sid [(16-1048575)]",
+ NO_STR
+ "Segment Routing Policy Binding-SID\n"
+ "SR Policy Binding-SID label\n")
+{
+ nb_cli_enqueue_change(vty, "./binding-sid", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_srte_policy_binding_sid(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " binding-sid %s\n", yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path
+ */
+DEFPY(srte_policy_candidate_exp,
+ srte_policy_candidate_exp_cmd,
+ "candidate-path preference (0-4294967295)$preference name WORD$name \
+ explicit segment-list WORD$list_name",
+ "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"
+ "Explicit Path\n"
+ "List of SIDs\n"
+ "Name of the Segment List\n")
+{
+ nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, preference_str);
+ nb_cli_enqueue_change(vty, "./name", NB_OP_MODIFY, name);
+ nb_cli_enqueue_change(vty, "./protocol-origin", NB_OP_MODIFY, "local");
+ nb_cli_enqueue_change(vty, "./originator", NB_OP_MODIFY, "config");
+ nb_cli_enqueue_change(vty, "./type", NB_OP_MODIFY, "explicit");
+ nb_cli_enqueue_change(vty, "./segment-list-name", NB_OP_MODIFY,
+ list_name);
+ return nb_cli_apply_changes(vty, "./candidate-path[preference='%s']",
+ preference_str);
+}
+
+DEFPY_NOSH(
+ srte_policy_candidate_dyn,
+ srte_policy_candidate_dyn_cmd,
+ "candidate-path preference (0-4294967295)$preference name WORD$name 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")
+{
+ char xpath[XPATH_CANDIDATE_BASELEN];
+ int ret;
+
+ snprintf(xpath, sizeof(xpath), "%s/candidate-path[preference='%s']",
+ VTY_CURR_XPATH, preference_str);
+
+ nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, preference_str);
+ nb_cli_enqueue_change(vty, "./name", NB_OP_MODIFY, name);
+ nb_cli_enqueue_change(vty, "./protocol-origin", NB_OP_MODIFY, "local");
+ nb_cli_enqueue_change(vty, "./originator", NB_OP_MODIFY, "config");
+ nb_cli_enqueue_change(vty, "./type", NB_OP_MODIFY, "dynamic");
+ ret = nb_cli_apply_changes(vty, "./candidate-path[preference='%s']",
+ preference_str);
+
+ if (ret == CMD_SUCCESS)
+ VTY_PUSH_XPATH(SR_CANDIDATE_DYN_NODE, xpath);
+
+ return ret;
+}
+
+DEFPY(srte_candidate_bandwidth,
+ srte_candidate_bandwidth_cmd,
+ "bandwidth BANDWIDTH$value [required$required]",
+ "Define a bandwidth constraint\n"
+ "Bandwidth value\n"
+ "Required constraint\n")
+{
+ nb_cli_enqueue_change(vty, "./constraints/bandwidth/required",
+ NB_OP_MODIFY, required ? "true" : "false");
+ nb_cli_enqueue_change(vty, "./constraints/bandwidth/value",
+ NB_OP_MODIFY, value);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_no_bandwidth,
+ srte_candidate_no_bandwidth_cmd,
+ "no bandwidth [BANDWIDTH$value] [required$required]",
+ NO_STR
+ "Remove a bandwidth constraint\n"
+ "Bandwidth value\n"
+ "Required constraint\n")
+{
+ nb_cli_enqueue_change(vty, "./constraints/bandwidth", NB_OP_DESTROY,
+ NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_affinity_filter,
+ srte_candidate_affinity_filter_cmd,
+ "affinity {exclude-any|include-any|include-all}$type BITPATTERN$value",
+ "Affinity constraint\n"
+ "Exclude any matching link\n"
+ "Include any matching link\n"
+ "Include all matching links\n"
+ "Attribute filter bit pattern as an hexadecimal value from 0x00000000 to 0xFFFFFFFF\n")
+{
+ uint32_t filter;
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+ char decimal_value[11];
+
+ if (sscanf(value, "0x%x", &filter) != 1) {
+ vty_out(vty, "affinity type: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ snprintf(decimal_value, sizeof(decimal_value), "%u", filter);
+ snprintf(xpath, sizeof(xpath), "./constraints/affinity/%s", type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, decimal_value);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_no_affinity_filter,
+ srte_candidate_no_affinity_filter_cmd,
+ "no affinity {exclude-any|include-any|include-all}$type [BITPATTERN$value]",
+ NO_STR
+ "Affinity constraint\n"
+ "Exclude any matching link\n"
+ "Include any matching link\n"
+ "Include all matching links\n"
+ "Attribute filter bit pattern as an hexadecimal value from 0x00000000 to 0xFFFFFFFF\n")
+{
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), "./constraints/affinity/%s", type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_metric,
+ srte_candidate_metric_cmd,
+ "metric [bound$bound] <igp|te|hc|abc|lmll|cigp|cte|pigp|pte|phc|msd|pd|pdv|pl|ppd|ppdv|ppl|nap|nlp|dc|bnc>$type METRIC$value [required$required]",
+ "Define a metric constraint\n"
+ "If the metric is bounded\n"
+ "IGP metric\n"
+ "TE metric\n"
+ "Hop Counts\n"
+ "Aggregate bandwidth consumption\n"
+ "Load of the most loaded link\n"
+ "Cumulative IGP cost\n"
+ "Cumulative TE cost\n"
+ "P2MP IGP metric\n"
+ "P2MP TE metric\n"
+ "P2MP hop count metric\n"
+ "Segment-ID (SID) Depth.\n"
+ "Path Delay metric\n"
+ "Path Delay Variation metric\n"
+ "Path Loss metric\n"
+ "P2MP Path Delay metric\n"
+ "P2MP Path Delay variation metric\n"
+ "P2MP Path Loss metric\n"
+ "Number of adaptations on a path\n"
+ "Number of layers on a path\n"
+ "Domain Count metric\n"
+ "Border Node Count metric\n"
+ "Metric value\n"
+ "Required constraint\n")
+{
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+ snprintf(xpath, sizeof(xpath), "./constraints/metrics[type='%s']/value",
+ type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, value);
+ snprintf(xpath, sizeof(xpath),
+ "./constraints/metrics[type='%s']/is-bound", type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ (bound != NULL) ? "true" : "false");
+ snprintf(xpath, sizeof(xpath),
+ "./constraints/metrics[type='%s']/required", type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ required ? "true" : "false");
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_no_metric,
+ srte_candidate_no_metric_cmd,
+ "no metric [bound] <igp|te|hc|abc|lmll|cigp|cte|pigp|pte|phc|msd|pd|pdv|pl|ppd|ppdv|ppl|nap|nlp|dc|bnc>$type [METRIC$value] [required$required]",
+ NO_STR
+ "Remove a metric constraint\n"
+ "If the metric is bounded\n"
+ "IGP metric\n"
+ "TE metric\n"
+ "Hop Counts\n"
+ "Aggregate bandwidth consumption\n"
+ "Load of the most loaded link\n"
+ "Cumulative IGP cost\n"
+ "Cumulative TE cost\n"
+ "P2MP IGP metric\n"
+ "P2MP TE metric\n"
+ "P2MP hop count metric\n"
+ "Segment-ID (SID) Depth.\n"
+ "Path Delay metric\n"
+ "Path Delay Variation metric\n"
+ "Path Loss metric\n"
+ "P2MP Path Delay metric\n"
+ "P2MP Path Delay variation metric\n"
+ "P2MP Path Loss metric\n"
+ "Number of adaptations on a path\n"
+ "Number of layers on a path\n"
+ "Domain Count metric\n"
+ "Border Node Count metric\n"
+ "Metric value\n"
+ "Required constraint\n")
+{
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+ snprintf(xpath, sizeof(xpath), "./constraints/metrics[type='%s']",
+ type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_policy_no_candidate,
+ srte_policy_no_candidate_cmd,
+ "no candidate-path\
+ preference (0-4294967295)$preference\
+ [name WORD\
+ <\
+ explicit segment-list WORD\
+ |dynamic\
+ >]",
+ NO_STR
+ "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"
+ "Explicit Path\n"
+ "List of SIDs\n"
+ "Name of the Segment List\n"
+ "Dynamic Path\n")
+{
+ nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, "./candidate-path[preference='%s']",
+ preference_str);
+}
+
+DEFPY(srte_candidate_objfun,
+ srte_candidate_objfun_cmd,
+ "objective-function <mcp|mlp|mbp|mbc|mll|mcc|spt|mct|mplp|mup|mrup|mtd|mbn|mctd|msl|mss|msn>$type [required$required]",
+ "Define an objective function constraint\n"
+ "Minimum Cost Path\n"
+ "Minimum Load Path\n"
+ "Maximum residual Bandwidth Path\n"
+ "Minimize aggregate Bandwidth Consumption\n"
+ "Minimize the Load of the most loaded Link\n"
+ "Minimize the Cumulative Cost of a set of paths\n"
+ "Shortest Path Tree\n"
+ "Minimum Cost Tree\n"
+ "Minimum Packet Loss Path\n"
+ "Maximum Under-Utilized Path\n"
+ "Maximum Reserved Under-Utilized Path\n"
+ "Minimize the number of Transit Domains\n"
+ "Minimize the number of Border Nodes\n"
+ "Minimize the number of Common Transit Domains\n"
+ "Minimize the number of Shared Links\n"
+ "Minimize the number of Shared SRLGs\n"
+ "Minimize the number of Shared Nodes\n"
+ "Required constraint\n")
+{
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+ nb_cli_enqueue_change(vty, "./constraints/objective-function",
+ NB_OP_DESTROY, NULL);
+ snprintf(xpath, sizeof(xpath),
+ "./constraints/objective-function/required");
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ required ? "true" : "false");
+ nb_cli_enqueue_change(vty, "./constraints/objective-function/type",
+ NB_OP_MODIFY, type);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_no_objfun,
+ srte_candidate_no_objfun_cmd,
+ "no objective-function [<mcp|mlp|mbp|mbc|mll|mcc|spt|mct|mplp|mup|mrup|mtd|mbn|mctd|msl|mss|msn>] [required$required]",
+ NO_STR
+ "Remove an objective function constraint\n"
+ "Minimum Cost Path\n"
+ "Minimum Load Path\n"
+ "Maximum residual Bandwidth Path\n"
+ "Minimize aggregate Bandwidth Consumption\n"
+ "Minimize the Load of the most loaded Link\n"
+ "Minimize the Cumulative Cost of a set of paths\n"
+ "Shortest Path Tree\n"
+ "Minimum Cost Tree\n"
+ "Minimum Packet Loss Path\n"
+ "Maximum Under-Utilized Path\n"
+ "Maximum Reserved Under-Utilized Path\n"
+ "Minimize the number of Transit Domains\n"
+ "Minimize the number of Border Nodes\n"
+ "Minimize the number of Common Transit Domains\n"
+ "Minimize the number of Shared Links\n"
+ "Minimize the number of Shared SRLGs\n"
+ "Minimize the number of Shared Nodes\n"
+ "Required constraint\n")
+{
+ nb_cli_enqueue_change(vty, "./constraints/objective-function",
+ NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+static const char *objfun_type_name(enum objfun_type type)
+{
+ switch (type) {
+ 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 NULL;
+ }
+}
+
+DEFPY_NOSH(show_debugging_pathd, show_debugging_pathd_cmd,
+ "show debugging [pathd]",
+ SHOW_STR
+ "State of each debugging option\n"
+ "pathd module debugging\n")
+{
+ /* nothing to do here */
+ return CMD_SUCCESS;
+}
+
+static const char *metric_type_name(enum srte_candidate_metric_type type)
+{
+ switch (type) {
+ case SRTE_CANDIDATE_METRIC_TYPE_IGP:
+ return "igp";
+ case SRTE_CANDIDATE_METRIC_TYPE_TE:
+ return "te";
+ case SRTE_CANDIDATE_METRIC_TYPE_HC:
+ return "hc";
+ case SRTE_CANDIDATE_METRIC_TYPE_ABC:
+ return "abc";
+ case SRTE_CANDIDATE_METRIC_TYPE_LMLL:
+ return "lmll";
+ case SRTE_CANDIDATE_METRIC_TYPE_CIGP:
+ return "cigp";
+ case SRTE_CANDIDATE_METRIC_TYPE_CTE:
+ return "cte";
+ case SRTE_CANDIDATE_METRIC_TYPE_PIGP:
+ return "pigp";
+ case SRTE_CANDIDATE_METRIC_TYPE_PTE:
+ return "pte";
+ case SRTE_CANDIDATE_METRIC_TYPE_PHC:
+ return "phc";
+ case SRTE_CANDIDATE_METRIC_TYPE_MSD:
+ return "msd";
+ case SRTE_CANDIDATE_METRIC_TYPE_PD:
+ return "pd";
+ case SRTE_CANDIDATE_METRIC_TYPE_PDV:
+ return "pdv";
+ case SRTE_CANDIDATE_METRIC_TYPE_PL:
+ return "pl";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPD:
+ return "ppd";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPDV:
+ return "ppdv";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPL:
+ return "ppl";
+ case SRTE_CANDIDATE_METRIC_TYPE_NAP:
+ return "nap";
+ case SRTE_CANDIDATE_METRIC_TYPE_NLP:
+ return "nlp";
+ case SRTE_CANDIDATE_METRIC_TYPE_DC:
+ return "dc";
+ case SRTE_CANDIDATE_METRIC_TYPE_BNC:
+ return "bnc";
+ default:
+ return NULL;
+ }
+}
+
+static void config_write_float(struct vty *vty, float value)
+{
+ if (fabs(truncf(value) - value) < FLT_EPSILON) {
+ vty_out(vty, " %d", (int)value);
+ return;
+ } else {
+ vty_out(vty, " %f", value);
+ }
+}
+
+static void config_write_metric(struct vty *vty,
+ enum srte_candidate_metric_type type,
+ float value, bool required, bool is_bound)
+{
+ const char *name = metric_type_name(type);
+ if (name == NULL)
+ return;
+ vty_out(vty, " metric %s%s", is_bound ? "bound " : "",
+ metric_type_name(type));
+ config_write_float(vty, value);
+ vty_out(vty, required ? " required" : "");
+ vty_out(vty, "\n");
+}
+
+static int config_write_metric_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct vty *vty = arg;
+ enum srte_candidate_metric_type type;
+ bool required, is_bound = false;
+ float value;
+
+ type = yang_dnode_get_enum(dnode, "./type");
+ value = (float)yang_dnode_get_dec64(dnode, "./value");
+ required = yang_dnode_get_bool(dnode, "./required");
+ if (yang_dnode_exists(dnode, "./is-bound"))
+ is_bound = yang_dnode_get_bool(dnode, "./is-bound");
+
+ config_write_metric(vty, type, value, required, is_bound);
+ return YANG_ITER_CONTINUE;
+}
+
+void cli_show_srte_policy_candidate_path(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+{
+ float bandwidth;
+ uint32_t affinity;
+ bool required;
+ enum objfun_type objfun_type;
+ const char *type = yang_dnode_get_string(dnode, "./type");
+
+ vty_out(vty, " candidate-path preference %s name %s %s",
+ yang_dnode_get_string(dnode, "./preference"),
+ yang_dnode_get_string(dnode, "./name"), type);
+ if (strmatch(type, "explicit"))
+ vty_out(vty, " segment-list %s",
+ yang_dnode_get_string(dnode, "./segment-list-name"));
+ vty_out(vty, "\n");
+
+ if (strmatch(type, "dynamic")) {
+ if (yang_dnode_exists(dnode, "./constraints/bandwidth")) {
+ bandwidth = (float)yang_dnode_get_dec64(
+ dnode, "./constraints/bandwidth/value");
+ required = yang_dnode_get_bool(
+ dnode, "./constraints/bandwidth/required");
+ vty_out(vty, " %sbandwidth",
+ required ? "required " : "");
+ config_write_float(vty, bandwidth);
+ vty_out(vty, "\n");
+ }
+ if (yang_dnode_exists(dnode,
+ "./constraints/affinity/exclude-any")) {
+ affinity = yang_dnode_get_uint32(
+ dnode, "./constraints/affinity/exclude-any");
+ vty_out(vty, " affinity exclude-any 0x%08x\n",
+ affinity);
+ }
+ if (yang_dnode_exists(dnode,
+ "./constraints/affinity/include-any")) {
+ affinity = yang_dnode_get_uint32(
+ dnode, "./constraints/affinity/include-any");
+ vty_out(vty, " affinity include-any 0x%08x\n",
+ affinity);
+ }
+ if (yang_dnode_exists(dnode,
+ "./constraints/affinity/include-all")) {
+ affinity = yang_dnode_get_uint32(
+ dnode, "./constraints/affinity/include-all");
+ vty_out(vty, " affinity include-all 0x%08x\n",
+ affinity);
+ }
+ yang_dnode_iterate(config_write_metric_cb, vty, dnode,
+ "./constraints/metrics");
+ if (yang_dnode_exists(dnode,
+ "./constraints/objective-function")) {
+ objfun_type = yang_dnode_get_enum(dnode,
+ "./constraints/objective-function/type");
+ required = yang_dnode_get_bool(dnode,
+ "./constraints/objective-function/required");
+ vty_out(vty, " objective-function %s%s\n",
+ objfun_type_name(objfun_type),
+ required ? " required" : "");
+ }
+ }
+}
+
+static int config_write_dnode(const struct lyd_node *dnode, void *arg)
+{
+ struct vty *vty = arg;
+
+ nb_cli_show_dnode_cmds(vty, (struct lyd_node *)dnode, false);
+
+ return YANG_ITER_CONTINUE;
+}
+
+int config_write_segment_routing(struct vty *vty)
+{
+ vty_out(vty, "segment-routing\n");
+ return 1;
+}
+
+int config_write_traffic_eng(struct vty *vty)
+{
+ vty_out(vty, " traffic-eng\n");
+ return 1;
+}
+
+int config_write_segment_lists(struct vty *vty)
+{
+ yang_dnode_iterate(config_write_dnode, vty, running_config->dnode,
+ "/frr-pathd:pathd/srte/segment-list");
+
+ return 1;
+}
+
+int config_write_sr_policies(struct vty *vty)
+{
+ yang_dnode_iterate(config_write_dnode, vty, running_config->dnode,
+ "/frr-pathd:pathd/srte/policy");
+
+ return 1;
+}
+
+void path_cli_init(void)
+{
+ 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_default(SEGMENT_ROUTING_NODE);
+ install_default(SR_TRAFFIC_ENG_NODE);
+ install_default(SR_SEGMENT_LIST_NODE);
+ install_default(SR_POLICY_NODE);
+ install_default(SR_CANDIDATE_DYN_NODE);
+
+ install_element(ENABLE_NODE, &show_debugging_pathd_cmd);
+ install_element(ENABLE_NODE, &show_srte_policy_cmd);
+ install_element(ENABLE_NODE, &show_srte_policy_detail_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_no_segment_list_cmd);
+ install_element(SR_SEGMENT_LIST_NODE,
+ &srte_segment_list_segment_cmd);
+ install_element(SR_SEGMENT_LIST_NODE,
+ &srte_segment_list_no_segment_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &srte_policy_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &srte_no_policy_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_name_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_no_name_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_binding_sid_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_no_binding_sid_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_candidate_exp_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_candidate_dyn_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_no_candidate_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_bandwidth_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_no_bandwidth_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_affinity_filter_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_no_affinity_filter_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_metric_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_no_metric_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_objfun_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_no_objfun_cmd);
+}
diff --git a/pathd/path_debug.c b/pathd/path_debug.c
new file mode 100644
index 0000000000..df0550715a
--- /dev/null
+++ b/pathd/path_debug.c
@@ -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";
+ }
+}
diff --git a/pathd/path_debug.h b/pathd/path_debug.h
new file mode 100644
index 0000000000..d9cfcb6ba2
--- /dev/null
+++ b/pathd/path_debug.h
@@ -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_ \ No newline at end of file
diff --git a/pathd/path_errors.c b/pathd/path_errors.c
new file mode 100644
index 0000000000..f266b6b4ea
--- /dev/null
+++ b/pathd/path_errors.c
@@ -0,0 +1,43 @@
+/*
+ * 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 = END_FERR,
+ }
+};
+
+static struct log_ref ferr_path_warn[] = {
+ {
+ .code = END_FERR,
+ }
+};
+/* clang-format on */
+
+void path_error_init(void)
+{
+ log_ref_add(ferr_path_err);
+ log_ref_add(ferr_path_warn);
+}
diff --git a/pathd/path_errors.h b/pathd/path_errors.h
new file mode 100644
index 0000000000..366878f300
--- /dev/null
+++ b/pathd/path_errors.h
@@ -0,0 +1,30 @@
+/*
+ * 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,
+};
+
+extern void path_error_init(void);
+
+#endif
diff --git a/pathd/path_main.c b/pathd/path_main.c
new file mode 100644
index 0000000000..8b7d4aba48
--- /dev/null
+++ b/pathd/path_main.c
@@ -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;
+}
diff --git a/pathd/path_memory.c b/pathd/path_memory.c
new file mode 100644
index 0000000000..ad4904a298
--- /dev/null
+++ b/pathd/path_memory.c
@@ -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")
diff --git a/pathd/path_memory.h b/pathd/path_memory.h
new file mode 100644
index 0000000000..e2f6787f66
--- /dev/null
+++ b/pathd/path_memory.h
@@ -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_ */
diff --git a/pathd/path_nb.c b/pathd/path_nb.c
new file mode 100644
index 0000000000..a210e31b9c
--- /dev/null
+++ b/pathd/path_nb.c
@@ -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;
+}
diff --git a/pathd/path_nb.h b/pathd/path_nb.h
new file mode 100644
index 0000000000..3a0b3863ce
--- /dev/null
+++ b/pathd/path_nb.h
@@ -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_ */
diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c
new file mode 100644
index 0000000000..669db169ae
--- /dev/null
+++ b/pathd/path_nb_config.c
@@ -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;
+}
diff --git a/pathd/path_nb_state.c b/pathd/path_nb_state.c
new file mode 100644
index 0000000000..60f04f45c1
--- /dev/null
+++ b/pathd/path_nb_state.c
@@ -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);
+}
diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c
new file mode 100644
index 0000000000..f0a22e4392
--- /dev/null
+++ b/pathd/path_zebra.c
@@ -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();
+}
diff --git a/pathd/path_zebra.h b/pathd/path_zebra.h
new file mode 100644
index 0000000000..42a7123dd4
--- /dev/null
+++ b/pathd/path_zebra.h
@@ -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_ */
diff --git a/pathd/pathd.c b/pathd/pathd.c
new file mode 100644
index 0000000000..2e2fa86714
--- /dev/null
+++ b/pathd/pathd.c
@@ -0,0 +1,1127 @@
+/*
+ * 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 "log.h"
+#include "lib_errors.h"
+
+#include "pathd/pathd.h"
+#include "pathd/path_memory.h"
+#include "pathd/path_zebra.h"
+#include "pathd/path_debug.h"
+
+#define HOOK_DELAY 3
+
+DEFINE_MTYPE_STATIC(PATHD, PATH_SEGMENT_LIST, "Segment List")
+DEFINE_MTYPE_STATIC(PATHD, PATH_SR_POLICY, "SR Policy")
+DEFINE_MTYPE_STATIC(PATHD, PATH_SR_CANDIDATE, "SR Policy candidate path")
+
+DEFINE_HOOK(pathd_candidate_created, (struct srte_candidate * candidate),
+ (candidate))
+DEFINE_HOOK(pathd_candidate_updated, (struct srte_candidate * candidate),
+ (candidate))
+DEFINE_HOOK(pathd_candidate_removed, (struct srte_candidate * candidate),
+ (candidate))
+
+static void trigger_pathd_candidate_created(struct srte_candidate *candidate);
+static int trigger_pathd_candidate_created_timer(struct thread *thread);
+static void trigger_pathd_candidate_updated(struct srte_candidate *candidate);
+static int trigger_pathd_candidate_updated_timer(struct thread *thread);
+static void trigger_pathd_candidate_removed(struct srte_candidate *candidate);
+static const char *
+srte_candidate_metric_name(enum srte_candidate_metric_type type);
+
+static void srte_set_metric(struct srte_metric *metric, float value,
+ bool required, bool is_bound, bool is_computed);
+static void srte_unset_metric(struct srte_metric *metric);
+
+
+/* Generate rb-tree of Segment List Segment instances. */
+static inline int srte_segment_entry_compare(const struct srte_segment_entry *a,
+ const struct srte_segment_entry *b)
+{
+ return a->index - b->index;
+}
+RB_GENERATE(srte_segment_entry_head, srte_segment_entry, entry,
+ srte_segment_entry_compare)
+
+/* Generate rb-tree of Segment List instances. */
+static inline int srte_segment_list_compare(const struct srte_segment_list *a,
+ const struct srte_segment_list *b)
+{
+ return strcmp(a->name, b->name);
+}
+RB_GENERATE(srte_segment_list_head, srte_segment_list, entry,
+ srte_segment_list_compare)
+
+struct srte_segment_list_head srte_segment_lists =
+ RB_INITIALIZER(&srte_segment_lists);
+
+/* Generate rb-tree of Candidate Path instances. */
+static inline int srte_candidate_compare(const struct srte_candidate *a,
+ const struct srte_candidate *b)
+{
+ return a->preference - b->preference;
+}
+RB_GENERATE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare)
+
+/* Generate rb-tree of SR Policy instances. */
+static inline int srte_policy_compare(const struct srte_policy *a,
+ const struct srte_policy *b)
+{
+ return sr_policy_compare(&a->endpoint, &b->endpoint, a->color,
+ b->color);
+}
+RB_GENERATE(srte_policy_head, srte_policy, entry, srte_policy_compare)
+
+struct srte_policy_head srte_policies = RB_INITIALIZER(&srte_policies);
+
+/**
+ * Adds a segment list to pathd.
+ *
+ * @param name The name of the segment list to add
+ * @return The added segment list
+ */
+struct srte_segment_list *srte_segment_list_add(const char *name)
+{
+ struct srte_segment_list *segment_list;
+
+ segment_list = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment_list));
+ strlcpy(segment_list->name, name, sizeof(segment_list->name));
+ RB_INIT(srte_segment_entry_head, &segment_list->segments);
+ RB_INSERT(srte_segment_list_head, &srte_segment_lists, segment_list);
+
+ return segment_list;
+}
+
+/**
+ * Deletes a segment list from pathd.
+ *
+ * The given segment list structure will be freed and should not be used anymore
+ * after calling this function.
+ *
+ * @param segment_list the segment list to remove from pathd.
+ */
+void srte_segment_list_del(struct srte_segment_list *segment_list)
+{
+ struct srte_segment_entry *segment, *safe_seg;
+ RB_FOREACH_SAFE (segment, srte_segment_entry_head,
+ &segment_list->segments, safe_seg) {
+ srte_segment_entry_del(segment);
+ }
+ RB_REMOVE(srte_segment_list_head, &srte_segment_lists, segment_list);
+ XFREE(MTYPE_PATH_SEGMENT_LIST, segment_list);
+}
+
+/**
+ * Search for a segment list by name.
+ *
+ * @param name The name of the segment list to look for
+ * @return The segment list if found, NULL otherwise
+ */
+struct srte_segment_list *srte_segment_list_find(const char *name)
+{
+ struct srte_segment_list search;
+
+ strlcpy(search.name, name, sizeof(search.name));
+ return RB_FIND(srte_segment_list_head, &srte_segment_lists, &search);
+}
+
+/**
+ * Adds a segment to a segment list.
+ *
+ * @param segment_list The segment list the segment should be added to
+ * @param index The index of the added segment in the segment list
+ * @return The added segment
+ */
+struct srte_segment_entry *
+srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index)
+{
+ struct srte_segment_entry *segment;
+
+ segment = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment));
+ segment->segment_list = segment_list;
+ segment->index = index;
+ RB_INSERT(srte_segment_entry_head, &segment_list->segments, segment);
+
+ return segment;
+}
+
+/**
+ * Deletes a segment from a segment list.
+ *
+ * @param segment The segment to be removed
+ */
+void srte_segment_entry_del(struct srte_segment_entry *segment)
+{
+ RB_REMOVE(srte_segment_entry_head, &segment->segment_list->segments,
+ segment);
+ XFREE(MTYPE_PATH_SEGMENT_LIST, segment);
+}
+
+/**
+ * Set the node or adjacency identifier of a segment.
+ *
+ * @param segment The segment for which the NAI should be set
+ * @param type The type of the NAI
+ * @param type The address of the node or the local address of the adjacency
+ * @param type The local interface index of the unumbered adjacency
+ * @param type The remote address of the adjacency
+ * @param type The remote interface index of the unumbered adjacency
+ */
+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)
+{
+ segment->nai_type = type;
+ memcpy(&segment->nai_local_addr, local_ip, sizeof(struct ipaddr));
+
+ 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:
+ memcpy(&segment->nai_remote_addr, remote_ip,
+ sizeof(struct ipaddr));
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
+ memcpy(&segment->nai_remote_addr, remote_ip,
+ sizeof(struct ipaddr));
+ segment->nai_local_iface = local_iface;
+ segment->nai_remote_iface = remote_iface;
+ break;
+ default:
+ 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;
+ }
+}
+
+/**
+ * Add a policy to pathd.
+ *
+ * WARNING: The color 0 is a special case as it is the no-color.
+ *
+ * @param color The color of the policy.
+ * @param endpoint The IP address of the policy endpoint
+ * @return The created policy
+ */
+struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint)
+{
+ struct srte_policy *policy;
+
+ policy = XCALLOC(MTYPE_PATH_SR_POLICY, sizeof(*policy));
+ policy->color = color;
+ policy->endpoint = *endpoint;
+ policy->binding_sid = MPLS_LABEL_NONE;
+ RB_INIT(srte_candidate_head, &policy->candidate_paths);
+ RB_INSERT(srte_policy_head, &srte_policies, policy);
+
+ return policy;
+}
+
+/**
+ * Delete a policy from pathd.
+ *
+ * The given policy structure will be freed and should never be used again
+ * after calling this function.
+ *
+ * @param policy The policy to be removed
+ */
+void srte_policy_del(struct srte_policy *policy)
+{
+ struct srte_candidate *candidate;
+
+ path_zebra_delete_sr_policy(policy);
+
+ path_zebra_release_label(policy->binding_sid);
+
+ while (!RB_EMPTY(srte_candidate_head, &policy->candidate_paths)) {
+ candidate =
+ RB_ROOT(srte_candidate_head, &policy->candidate_paths);
+ trigger_pathd_candidate_removed(candidate);
+ srte_candidate_del(candidate);
+ }
+
+ RB_REMOVE(srte_policy_head, &srte_policies, policy);
+ XFREE(MTYPE_PATH_SR_POLICY, policy);
+}
+
+/**
+ * Search for a policy by color and endpoint.
+ *
+ * WARNING: The color 0 is a special case as it is the no-color.
+ *
+ * @param color The color of the policy to look for
+ * @param endpoint The endpoint of the policy to look for
+ * @return The policy if found, NULL otherwise
+ */
+struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint)
+{
+ struct srte_policy search;
+
+ search.color = color;
+ search.endpoint = *endpoint;
+ return RB_FIND(srte_policy_head, &srte_policies, &search);
+}
+
+/**
+ * Update a policy binding SID.
+ *
+ * @param policy The policy for which the SID should be updated
+ * @param binding_sid The new binding SID for the given policy
+ */
+void srte_policy_update_binding_sid(struct srte_policy *policy,
+ uint32_t binding_sid)
+{
+ if (policy->binding_sid != MPLS_LABEL_NONE)
+ path_zebra_release_label(policy->binding_sid);
+
+ policy->binding_sid = binding_sid;
+
+ /* Reinstall the Binding-SID if necessary. */
+ if (policy->best_candidate)
+ path_zebra_add_sr_policy(
+ policy, policy->best_candidate->lsp->segment_list);
+}
+
+/**
+ * Gives the policy best candidate path.
+ *
+ * @param policy The policy we want the best candidate path from
+ * @return The best candidate path
+ */
+static struct srte_candidate *
+srte_policy_best_candidate(const struct srte_policy *policy)
+{
+ struct srte_candidate *candidate;
+
+ RB_FOREACH_REVERSE (candidate, srte_candidate_head,
+ &policy->candidate_paths) {
+ /* search for highest preference with existing segment list */
+ if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED)
+ && candidate->lsp->segment_list)
+ return candidate;
+ }
+
+ return NULL;
+}
+
+/**
+ * Apply changes defined by setting the policies, candidate paths
+ * and segment lists modification flags NEW, MODIFIED and DELETED.
+ *
+ * This allows the northbound code to delay all the side effects of adding
+ * modifying and deleting them to the end.
+ *
+ * Example of marking an object as modified:
+ * `SET_FLAG(obj->flags, F_XXX_MODIFIED)`
+ */
+void srte_apply_changes(void)
+{
+ struct srte_policy *policy, *safe_pol;
+ struct srte_segment_list *segment_list, *safe_sl;
+
+ RB_FOREACH_SAFE (policy, srte_policy_head, &srte_policies, safe_pol) {
+ if (CHECK_FLAG(policy->flags, F_POLICY_DELETED)) {
+ srte_policy_del(policy);
+ continue;
+ }
+ srte_policy_apply_changes(policy);
+ UNSET_FLAG(policy->flags, F_POLICY_NEW);
+ UNSET_FLAG(policy->flags, F_POLICY_MODIFIED);
+ }
+
+ RB_FOREACH_SAFE (segment_list, srte_segment_list_head,
+ &srte_segment_lists, safe_sl) {
+ if (CHECK_FLAG(segment_list->flags, F_SEGMENT_LIST_DELETED)) {
+ srte_segment_list_del(segment_list);
+ continue;
+ }
+ UNSET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW);
+ UNSET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
+ }
+}
+
+/**
+ * Apply changes defined by setting the given policy and its candidate paths
+ * modification flags NEW, MODIFIED and DELETED.
+ *
+ * In moste cases `void srte_apply_changes(void)` should be used instead,
+ * this function will not handle the changes of segment lists used by the
+ * policy.
+ *
+ * @param policy The policy changes has to be applied to.
+ */
+void srte_policy_apply_changes(struct srte_policy *policy)
+{
+ struct srte_candidate *candidate, *safe;
+ struct srte_candidate *old_best_candidate;
+ struct srte_candidate *new_best_candidate;
+ char endpoint[46];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ /* Get old and new best candidate path. */
+ old_best_candidate = policy->best_candidate;
+ new_best_candidate = srte_policy_best_candidate(policy);
+
+ if (new_best_candidate != old_best_candidate) {
+ /* TODO: add debug guard. */
+ zlog_debug(
+ "SR-TE(%s, %u): best candidate changed from %s to %s",
+ endpoint, policy->color,
+ old_best_candidate ? old_best_candidate->name : "none",
+ new_best_candidate ? new_best_candidate->name : "none");
+
+ if (old_best_candidate) {
+ policy->best_candidate = NULL;
+ UNSET_FLAG(old_best_candidate->flags, F_CANDIDATE_BEST);
+ SET_FLAG(old_best_candidate->flags,
+ F_CANDIDATE_MODIFIED);
+
+ /*
+ * Rely on replace semantics if there's a new best
+ * candidate.
+ */
+ if (!new_best_candidate)
+ path_zebra_delete_sr_policy(policy);
+ }
+ if (new_best_candidate) {
+ policy->best_candidate = new_best_candidate;
+ SET_FLAG(new_best_candidate->flags, F_CANDIDATE_BEST);
+ SET_FLAG(new_best_candidate->flags,
+ F_CANDIDATE_MODIFIED);
+
+ path_zebra_add_sr_policy(
+ policy, new_best_candidate->lsp->segment_list);
+ }
+ } else if (new_best_candidate) {
+ /* The best candidate path did not change, but some of its
+ * attributes or its segment list may have changed.
+ */
+
+ bool candidate_changed = CHECK_FLAG(new_best_candidate->flags,
+ F_CANDIDATE_MODIFIED);
+ bool segment_list_changed =
+ new_best_candidate->lsp->segment_list
+ && CHECK_FLAG(
+ new_best_candidate->lsp->segment_list->flags,
+ F_SEGMENT_LIST_MODIFIED);
+
+ if (candidate_changed || segment_list_changed) {
+ /* TODO: add debug guard. */
+ zlog_debug("SR-TE(%s, %u): best candidate %s changed",
+ endpoint, policy->color,
+ new_best_candidate->name);
+
+ path_zebra_add_sr_policy(
+ policy, new_best_candidate->lsp->segment_list);
+ }
+ }
+
+ RB_FOREACH_SAFE (candidate, srte_candidate_head,
+ &policy->candidate_paths, safe) {
+ if (CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED)) {
+ trigger_pathd_candidate_removed(candidate);
+ srte_candidate_del(candidate);
+ continue;
+ } else if (CHECK_FLAG(candidate->flags, F_CANDIDATE_NEW)) {
+ trigger_pathd_candidate_created(candidate);
+ } else if (CHECK_FLAG(candidate->flags, F_CANDIDATE_MODIFIED)) {
+ trigger_pathd_candidate_updated(candidate);
+ } else if (candidate->lsp->segment_list
+ && CHECK_FLAG(candidate->lsp->segment_list->flags,
+ F_SEGMENT_LIST_MODIFIED)) {
+ trigger_pathd_candidate_updated(candidate);
+ }
+
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_NEW);
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ }
+}
+
+/**
+ * Adds a candidate path to a policy.
+ *
+ * @param policy The policy the candidate path should be added to
+ * @param preference The preference of the candidate path to be added
+ * @return The added candidate path
+ */
+struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
+ uint32_t preference)
+{
+ struct srte_candidate *candidate;
+ struct srte_lsp *lsp;
+
+ candidate = XCALLOC(MTYPE_PATH_SR_CANDIDATE, sizeof(*candidate));
+ lsp = XCALLOC(MTYPE_PATH_SR_CANDIDATE, sizeof(*lsp));
+
+ candidate->preference = preference;
+ candidate->policy = policy;
+ candidate->type = SRTE_CANDIDATE_TYPE_UNDEFINED;
+ candidate->discriminator = rand();
+
+ lsp->candidate = candidate;
+ candidate->lsp = lsp;
+
+ RB_INSERT(srte_candidate_head, &policy->candidate_paths, candidate);
+
+ return candidate;
+}
+
+/**
+ * Deletes a candidate.
+ *
+ * The corresponding LSP will be removed alongside the candidate path.
+ * The given candidate will be freed and shouldn't be used anymore after the
+ * calling this function.
+ *
+ * @param candidate The candidate path to delete
+ */
+void srte_candidate_del(struct srte_candidate *candidate)
+{
+ struct srte_policy *srte_policy = candidate->policy;
+
+ RB_REMOVE(srte_candidate_head, &srte_policy->candidate_paths,
+ candidate);
+
+ XFREE(MTYPE_PATH_SR_CANDIDATE, candidate->lsp);
+ XFREE(MTYPE_PATH_SR_CANDIDATE, candidate);
+}
+
+/**
+ * Sets the bandwidth constraint of given candidate path.
+ *
+ * The corresponding LSP will be changed too.
+ *
+ * @param candidate The candidate path of which the bandwidth should be changed
+ * @param bandwidth The Bandwidth constraint to set to the candidate path
+ * @param required If the constraint is required (true) or only desired (false)
+ */
+void srte_candidate_set_bandwidth(struct srte_candidate *candidate,
+ float bandwidth, bool required)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s %sconfig bandwidth set to %f B/s",
+ endpoint, policy->color, candidate->name,
+ required ? "required " : "", bandwidth);
+ SET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required);
+ candidate->bandwidth = bandwidth;
+
+ srte_lsp_set_bandwidth(candidate->lsp, bandwidth, required);
+}
+
+/**
+ * Sets the bandwidth constraint of the given LSP.
+ *
+ * The changes will not be shown as part of the running configuration.
+ *
+ * @param lsp The lsp of which the bandwidth should be changed
+ * @param bandwidth The Bandwidth constraint to set to the candidate path
+ * @param required If the constraint is required (true) or only desired (false)
+ */
+void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth,
+ bool required)
+{
+ struct srte_candidate *candidate = lsp->candidate;
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s %slsp bandwidth set to %f B/s",
+ endpoint, policy->color, candidate->name,
+ required ? "required" : "", bandwidth);
+ SET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ COND_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required);
+ lsp->bandwidth = bandwidth;
+}
+
+/**
+ * Remove a candidate path bandwidth constraint.
+ *
+ * The corresponding LSP will be changed too.
+ *
+ * @param candidate The candidate path of which the bandwidth should be removed
+ */
+void srte_candidate_unset_bandwidth(struct srte_candidate *candidate)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s config bandwidth unset",
+ endpoint, policy->color, candidate->name);
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH);
+ candidate->bandwidth = 0;
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ srte_lsp_unset_bandwidth(candidate->lsp);
+}
+
+/**
+ * Remove an LSP bandwidth constraint.
+ *
+ * The changes will not be shown as part of the running configuration.
+ *
+ * @param lsp The lsp of which the bandwidth should be changed
+ */
+void srte_lsp_unset_bandwidth(struct srte_lsp *lsp)
+{
+ struct srte_candidate *candidate = lsp->candidate;
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s lsp bandwidth unset", endpoint,
+ policy->color, candidate->name);
+ UNSET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ UNSET_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ lsp->bandwidth = 0;
+}
+
+/**
+ * Sets a candidate path metric constraint.
+ *
+ * The corresponding LSP will be changed too.
+ *
+ * @param candidate The candidate path of which the metric should be changed
+ * @param type The metric type
+ * @param value The metric value
+ * @param required If the constraint is required (true) or only desired (false)
+ * @param is_bound If the metric is an indicative value or a strict upper bound
+ * @param is_computed If the metric was computed or configured
+ */
+void srte_candidate_set_metric(struct srte_candidate *candidate,
+ enum srte_candidate_metric_type type,
+ float value, bool required, bool is_bound,
+ bool is_computed)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f "
+ "(is-bound: %s; is_computed: %s)",
+ endpoint, policy->color, candidate->name,
+ required ? "required " : "", srte_candidate_metric_name(type),
+ type, value, is_bound ? "true" : "false",
+ is_computed ? "true" : "false");
+ assert((type > 0) && (type <= MAX_METRIC_TYPE));
+ srte_set_metric(&candidate->metrics[type - 1], value, required,
+ is_bound, is_computed);
+ srte_lsp_set_metric(candidate->lsp, type, value, required, is_bound,
+ is_computed);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+}
+
+/**
+ * Sets an LSP metric constraint.
+ *
+ * The changes will not be shown as part of the running configuration.
+ *
+ * @param lsp The LSP of which the metric should be changed
+ * @param type The metric type
+ * @param value The metric value
+ * @param required If the constraint is required (true) or only desired (false)
+ * @param is_bound If the metric is an indicative value or a strict upper bound
+ * @param is_computed If the metric was computed or configured
+ */
+void srte_lsp_set_metric(struct srte_lsp *lsp,
+ enum srte_candidate_metric_type type, float value,
+ bool required, bool is_bound, bool is_computed)
+{
+ struct srte_candidate *candidate = lsp->candidate;
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f "
+ "(is-bound: %s; is_computed: %s)",
+ endpoint, policy->color, candidate->name,
+ required ? "required " : "", srte_candidate_metric_name(type),
+ type, value, is_bound ? "true" : "false",
+ is_computed ? "true" : "false");
+ assert((type > 0) && (type <= MAX_METRIC_TYPE));
+ srte_set_metric(&lsp->metrics[type - 1], value, required, is_bound,
+ is_computed);
+}
+
+void srte_set_metric(struct srte_metric *metric, float value, bool required,
+ bool is_bound, bool is_computed)
+{
+ SET_FLAG(metric->flags, F_METRIC_IS_DEFINED);
+ COND_FLAG(metric->flags, F_METRIC_IS_REQUIRED, required);
+ COND_FLAG(metric->flags, F_METRIC_IS_BOUND, is_bound);
+ COND_FLAG(metric->flags, F_METRIC_IS_COMPUTED, is_computed);
+ metric->value = value;
+}
+
+/**
+ * Removes a candidate path metric constraint.
+ *
+ * The corresponding LSP will be changed too.
+ *
+ * @param candidate The candidate path from which the metric should be removed
+ * @param type The metric type
+ */
+void srte_candidate_unset_metric(struct srte_candidate *candidate,
+ enum srte_candidate_metric_type type)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s config metric %s (%u) unset",
+ endpoint, policy->color, candidate->name,
+ srte_candidate_metric_name(type), type);
+ assert((type > 0) && (type <= MAX_METRIC_TYPE));
+ srte_unset_metric(&candidate->metrics[type - 1]);
+ srte_lsp_unset_metric(candidate->lsp, type);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+}
+
+/**
+ * Removes a candidate path metric constraint.
+ *
+ * The changes will not be shown as part of the running configuration.
+ *
+ * @param lsp The LSP from which the metric should be removed
+ * @param type The metric type
+ */
+void srte_lsp_unset_metric(struct srte_lsp *lsp,
+ enum srte_candidate_metric_type type)
+{
+ struct srte_candidate *candidate = lsp->candidate;
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s lsp metric %s (%u) unset",
+ endpoint, policy->color, candidate->name,
+ srte_candidate_metric_name(type), type);
+ assert((type > 0) && (type <= MAX_METRIC_TYPE));
+ srte_unset_metric(&lsp->metrics[type - 1]);
+}
+
+void srte_unset_metric(struct srte_metric *metric)
+{
+ UNSET_FLAG(metric->flags, F_METRIC_IS_DEFINED);
+ UNSET_FLAG(metric->flags, F_METRIC_IS_BOUND);
+ UNSET_FLAG(metric->flags, F_METRIC_IS_COMPUTED);
+ metric->value = 0;
+}
+
+/**
+ * Sets a candidate path objective function.
+ *
+ * @param candidate The candidate path of which the OF should be changed
+ * @param required If the constraint is required (true) or only desired (false)
+ * @param type The objective function type
+ */
+void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required,
+ enum objfun_type type)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ candidate->objfun = type;
+ SET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN);
+ COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN, required);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ zlog_debug("SR-TE(%s, %u): candidate %s %sobjective function set to %s",
+ endpoint, policy->color, candidate->name,
+ required ? "required " : "", objfun_type_name(type));
+}
+
+/**
+ * Removed the objective function constraint from a candidate path.
+ *
+ * @param candidate The candidate path from which the OF should be removed
+ */
+void srte_candidate_unset_objfun(struct srte_candidate *candidate)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN);
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ candidate->objfun = OBJFUN_UNDEFINED;
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s objective functions preferences unset",
+ endpoint, policy->color, candidate->name);
+}
+
+static uint32_t filter_type_to_flag(enum affinity_filter_type type)
+{
+ switch (type) {
+ case AFFINITY_FILTER_EXCLUDE_ANY:
+ return F_CANDIDATE_HAS_EXCLUDE_ANY;
+ case AFFINITY_FILTER_INCLUDE_ANY:
+ return F_CANDIDATE_HAS_INCLUDE_ANY;
+ case AFFINITY_FILTER_INCLUDE_ALL:
+ return F_CANDIDATE_HAS_INCLUDE_ALL;
+ default:
+ return 0;
+ }
+}
+
+static const char *filter_type_name(enum affinity_filter_type type)
+{
+ switch (type) {
+ case AFFINITY_FILTER_EXCLUDE_ANY:
+ return "exclude-any";
+ case AFFINITY_FILTER_INCLUDE_ANY:
+ return "include-any";
+ case AFFINITY_FILTER_INCLUDE_ALL:
+ return "include-all";
+ default:
+ return "unknown";
+ }
+}
+
+/**
+ * Sets a candidate path affinity filter constraint.
+ *
+ * @param candidate The candidate path of which the constraint should be changed
+ * @param type The affinity constraint type to set
+ * @param filter The bitmask filter of the constraint
+ */
+void srte_candidate_set_affinity_filter(struct srte_candidate *candidate,
+ enum affinity_filter_type type,
+ uint32_t filter)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ assert(type > AFFINITY_FILTER_UNDEFINED);
+ assert(type <= MAX_AFFINITY_FILTER_TYPE);
+ SET_FLAG(candidate->flags, filter_type_to_flag(type));
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ candidate->affinity_filters[type - 1] = filter;
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s affinity filter %s set to 0x%08x",
+ endpoint, policy->color, candidate->name,
+ filter_type_name(type), filter);
+}
+
+/**
+ * Removes a candidate path affinity filter constraint.
+ *
+ * @param candidate The candidate path from which the constraint should be
+ * removed
+ * @param type The affinity constraint type to remove
+ */
+void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate,
+ enum affinity_filter_type type)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ assert(type > AFFINITY_FILTER_UNDEFINED);
+ assert(type <= MAX_AFFINITY_FILTER_TYPE);
+ UNSET_FLAG(candidate->flags, filter_type_to_flag(type));
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ candidate->affinity_filters[type - 1] = 0;
+ zlog_debug("SR-TE(%s, %u): candidate %s affinity filter %s unset",
+ endpoint, policy->color, candidate->name,
+ filter_type_name(type));
+}
+
+/**
+ * Searches for a candidate path of the given policy.
+ *
+ * @param policy The policy to search for candidate path
+ * @param preference The preference of the candidate path you are looking for
+ * @return The candidate path if found, NULL otherwise
+ */
+struct srte_candidate *srte_candidate_find(struct srte_policy *policy,
+ uint32_t preference)
+{
+ struct srte_candidate search;
+
+ search.preference = preference;
+ return RB_FIND(srte_candidate_head, &policy->candidate_paths, &search);
+}
+
+/**
+ * Searches for a an entry of a given segment list.
+ *
+ * @param segment_list The segment list to search for the entry
+ * @param index The index of the entry you are looking for
+ * @return The segment list entry if found, NULL otherwise.
+ */
+struct srte_segment_entry *
+srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index)
+{
+ struct srte_segment_entry search;
+
+ search.index = index;
+ return RB_FIND(srte_segment_entry_head, &segment_list->segments,
+ &search);
+}
+
+/**
+ * Updates a candidate status.
+ *
+ * @param candidate The candidate of which the status should be updated
+ * @param status The new candidate path status
+ */
+void srte_candidate_status_update(struct srte_candidate *candidate, int status)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): zebra updated status to %d", endpoint,
+ policy->color, status);
+ switch (status) {
+ case ZEBRA_SR_POLICY_DOWN:
+ switch (policy->status) {
+ /* If the policy is GOING_UP, and zebra faild
+ to install it, we wait for zebra to retry */
+ /* TODO: Add some timeout after which we would
+ get is back to DOWN and remove the
+ policy */
+ case SRTE_POLICY_STATUS_GOING_UP:
+ case SRTE_POLICY_STATUS_DOWN:
+ return;
+ default:
+ zlog_debug("SR-TE(%s, %u): policy is DOWN", endpoint,
+ policy->color);
+ policy->status = SRTE_POLICY_STATUS_DOWN;
+ break;
+ }
+ break;
+ case ZEBRA_SR_POLICY_UP:
+ switch (policy->status) {
+ case SRTE_POLICY_STATUS_UP:
+ return;
+ default:
+ zlog_debug("SR-TE(%s, %u): policy is UP", endpoint,
+ policy->color);
+ policy->status = SRTE_POLICY_STATUS_UP;
+ break;
+ }
+ break;
+ }
+
+ trigger_pathd_candidate_updated(candidate);
+}
+
+/**
+ * Flags the segment lists from give originator for removal.
+ *
+ * The function srte_apply_changes must be called afterward for
+ * the segment list to be removed.
+ *
+ * @param originator The originator tag of the segment list to be marked
+ * @param force If the unset should be forced regardless of the originator
+ */
+void srte_candidate_unset_segment_list(const char *originator, bool force)
+{
+ if (originator == NULL) {
+ zlog_warn(
+ "Cannot unset segment list because originator is NULL");
+ return;
+ }
+
+ zlog_debug("Unset segment lists for originator %s", originator);
+
+ /* Iterate the policies, then iterate each policy's candidate path
+ * to check the candidate path's segment list originator */
+ struct srte_policy *policy;
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ zlog_debug("Unset segment lists checking policy %s",
+ policy->name);
+ struct srte_candidate *candidate;
+ RB_FOREACH (candidate, srte_candidate_head,
+ &policy->candidate_paths) {
+ zlog_debug("Unset segment lists checking candidate %s",
+ candidate->name);
+ if (candidate->lsp == NULL) {
+ continue;
+ }
+
+ /* The candidate->lsp->segment_list is operational data,
+ * configured by the PCE. We dont want to modify the
+ * candidate->segment_list,
+ * which is configuration data. */
+ struct srte_segment_list *segment_list =
+ candidate->lsp->segment_list;
+ if (segment_list == NULL) {
+ continue;
+ }
+
+ if (segment_list->protocol_origin
+ == SRTE_ORIGIN_LOCAL) {
+ zlog_warn(
+ "Cannot unset segment list %s because it "
+ "was created locally",
+ segment_list->name);
+ continue;
+ }
+
+ /* In the case of last pce,we force the unset
+ * because we don't have pce by prefix (TODO) is all
+ * 'global' */
+ if (strncmp(segment_list->originator, originator,
+ sizeof(segment_list->originator))
+ == 0
+ || force) {
+ zlog_debug("Unset segment list %s",
+ segment_list->name);
+ SET_FLAG(segment_list->flags,
+ F_SEGMENT_LIST_DELETED);
+ SET_FLAG(candidate->flags,
+ F_CANDIDATE_MODIFIED);
+ candidate->lsp->segment_list = NULL;
+ }
+ }
+ }
+}
+
+/**
+ * Gives a string representation of given protocol origin enum.
+ *
+ * @param origin The enum you want a string representation of
+ * @return The string representation of given enum
+ */
+const char *srte_origin2str(enum srte_protocol_origin origin)
+{
+ switch (origin) {
+ case SRTE_ORIGIN_PCEP:
+ return "PCEP";
+ case SRTE_ORIGIN_BGP:
+ return "BGP";
+ case SRTE_ORIGIN_LOCAL:
+ return "Local";
+ default:
+ return "Unknown";
+ }
+}
+
+void trigger_pathd_candidate_created(struct srte_candidate *candidate)
+{
+ /* The hook is called asynchronously to let the PCEP module
+ time to send a response to the PCE before receiving any updates from
+ pathd. In addition, a minimum amount of time need to pass before
+ the hook is called to prevent the hook to be called multiple times
+ from changing the candidate by hand with the console */
+ if (candidate->hook_timer != NULL)
+ return;
+ thread_add_timer(master, trigger_pathd_candidate_created_timer,
+ (void *)candidate, HOOK_DELAY, &candidate->hook_timer);
+}
+
+int trigger_pathd_candidate_created_timer(struct thread *thread)
+{
+ struct srte_candidate *candidate = THREAD_ARG(thread);
+ candidate->hook_timer = NULL;
+ return hook_call(pathd_candidate_created, candidate);
+}
+
+void trigger_pathd_candidate_updated(struct srte_candidate *candidate)
+{
+ /* The hook is called asynchronously to let the PCEP module
+ time to send a response to the PCE before receiving any updates from
+ pathd. In addition, a minimum amount of time need to pass before
+ the hook is called to prevent the hook to be called multiple times
+ from changing the candidate by hand with the console */
+ if (candidate->hook_timer != NULL)
+ return;
+ thread_add_timer(master, trigger_pathd_candidate_updated_timer,
+ (void *)candidate, HOOK_DELAY, &candidate->hook_timer);
+}
+
+int trigger_pathd_candidate_updated_timer(struct thread *thread)
+{
+ struct srte_candidate *candidate = THREAD_ARG(thread);
+ candidate->hook_timer = NULL;
+ return hook_call(pathd_candidate_updated, candidate);
+}
+
+void trigger_pathd_candidate_removed(struct srte_candidate *candidate)
+{
+ /* The hook needs to be call synchronously, otherwise the candidate
+ path will be already deleted when the handler is called */
+ if (candidate->hook_timer != NULL) {
+ thread_cancel(&candidate->hook_timer);
+ candidate->hook_timer = NULL;
+ }
+ hook_call(pathd_candidate_removed, candidate);
+}
+
+const char *srte_candidate_metric_name(enum srte_candidate_metric_type type)
+{
+ switch (type) {
+ case SRTE_CANDIDATE_METRIC_TYPE_IGP:
+ return "IGP";
+ case SRTE_CANDIDATE_METRIC_TYPE_TE:
+ return "TE";
+ case SRTE_CANDIDATE_METRIC_TYPE_HC:
+ return "HC";
+ case SRTE_CANDIDATE_METRIC_TYPE_ABC:
+ return "ABC";
+ case SRTE_CANDIDATE_METRIC_TYPE_LMLL:
+ return "LMLL";
+ case SRTE_CANDIDATE_METRIC_TYPE_CIGP:
+ return "CIGP";
+ case SRTE_CANDIDATE_METRIC_TYPE_CTE:
+ return "CTE";
+ case SRTE_CANDIDATE_METRIC_TYPE_PIGP:
+ return "PIGP";
+ case SRTE_CANDIDATE_METRIC_TYPE_PTE:
+ return "PTE";
+ case SRTE_CANDIDATE_METRIC_TYPE_PHC:
+ return "PHC";
+ case SRTE_CANDIDATE_METRIC_TYPE_MSD:
+ return "MSD";
+ case SRTE_CANDIDATE_METRIC_TYPE_PD:
+ return "PD";
+ case SRTE_CANDIDATE_METRIC_TYPE_PDV:
+ return "PDV";
+ case SRTE_CANDIDATE_METRIC_TYPE_PL:
+ return "PL";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPD:
+ return "PPD";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPDV:
+ return "PPDV";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPL:
+ return "PPL";
+ case SRTE_CANDIDATE_METRIC_TYPE_NAP:
+ return "NAP";
+ case SRTE_CANDIDATE_METRIC_TYPE_NLP:
+ return "NLP";
+ case SRTE_CANDIDATE_METRIC_TYPE_DC:
+ return "DC";
+ case SRTE_CANDIDATE_METRIC_TYPE_BNC:
+ return "BNC";
+ default:
+ return "UNKNOWN";
+ }
+}
diff --git a/pathd/pathd.conf.sample b/pathd/pathd.conf.sample
new file mode 100644
index 0000000000..fc7acafc1f
--- /dev/null
+++ b/pathd/pathd.conf.sample
@@ -0,0 +1,33 @@
+! 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
+ !
+ !
+ !
+!
diff --git a/pathd/pathd.h b/pathd/pathd.h
new file mode 100644
index 0000000000..4879239db8
--- /dev/null
+++ b/pathd/pathd.h
@@ -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_ */
diff --git a/pathd/subdir.am b/pathd/subdir.am
new file mode 100644
index 0000000000..23399b9d81
--- /dev/null
+++ b/pathd/subdir.am
@@ -0,0 +1,48 @@
+#
+# 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
+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
+
+noinst_HEADERS += \
+ pathd/path_debug.h \
+ pathd/path_errors.h \
+ pathd/path_memory.h \
+ pathd/path_nb.h \
+ pathd/path_zebra.h \
+ pathd/pathd.h \
+ # end
+
+pathd/path_cli_clippy.c: $(CLIPPY_DEPS)
+pathd/path_cli.$(OBJEXT): pathd/path_cli_clippy.c
+
+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)
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index 4ebc504b8a..02c272f47c 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -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 }
@@ -398,6 +405,11 @@ routing state through standard SNMP MIBs.
%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
diff --git a/tests/topotests/isis-sr-te-topo1/dst/zebra.conf b/tests/topotests/isis-sr-te-topo1/dst/zebra.conf
new file mode 100644
index 0000000000..e873ac8a5c
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/dst/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf b/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf
new file mode 100644
index 0000000000..efc03701b5
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf
new file mode 100644
index 0000000000..70ae1b07f5
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf b/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf
new file mode 100644
index 0000000000..911971496e
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf
@@ -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
+ !
+ !
+! \ No newline at end of file
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref
new file mode 100644
index 0000000000..d4b27d157d
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref
new file mode 100644
index 0000000000..5fe58d0824
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref
new file mode 100644
index 0000000000..4ef8d946f2
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref
@@ -0,0 +1,13 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": false
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref
new file mode 100644
index 0000000000..9b28f6a42b
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref
@@ -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
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref
new file mode 100644
index 0000000000..9b28f6a42b
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref
@@ -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
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref
new file mode 100644
index 0000000000..249117198a
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref
@@ -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
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref
new file mode 100644
index 0000000000..21f71f1254
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref
new file mode 100644
index 0000000000..3635c89efb
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref
new file mode 100644
index 0000000000..5712d210d4
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref
new file mode 100644
index 0000000000..5a76246e50
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref
@@ -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
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref
new file mode 100644
index 0000000000..09d5958305
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref
@@ -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
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref
new file mode 100644
index 0000000000..e26039b835
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref
@@ -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
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref
new file mode 100644
index 0000000000..01505c0318
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref
@@ -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
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf
new file mode 100644
index 0000000000..9d71d3005f
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf
new file mode 100644
index 0000000000..733f26bc62
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf
new file mode 100644
index 0000000000..dcb0686dc2
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf
new file mode 100644
index 0000000000..2395906cbf
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf
new file mode 100644
index 0000000000..3254529386
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf
new file mode 100644
index 0000000000..07a7867cbb
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..4945897e9d
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf
new file mode 100644
index 0000000000..b0fcdede07
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..4cfea1a59f
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf b/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf
new file mode 100644
index 0000000000..e72ee52fce
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf
new file mode 100644
index 0000000000..3be24ad24c
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf b/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf
new file mode 100644
index 0000000000..3bada7147c
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf
@@ -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
+ !
+ !
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref
new file mode 100644
index 0000000000..2bb000346f
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref
new file mode 100644
index 0000000000..348f7761eb
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref
new file mode 100644
index 0000000000..241c80bdd7
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref
@@ -0,0 +1,13 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": false
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref
new file mode 100644
index 0000000000..20ea69e386
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref
@@ -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
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref
new file mode 100644
index 0000000000..20ea69e386
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref
@@ -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
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref
new file mode 100644
index 0000000000..10cafe9091
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref
@@ -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
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref b/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref
new file mode 100644
index 0000000000..95bf995e2e
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf
new file mode 100644
index 0000000000..32c6e6c4e0
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py
new file mode 100755
index 0000000000..5e5c0f9fee
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py
@@ -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))
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index eaf7f90479..86f06b2af7 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -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):
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 20d60ebbef..d1e76866a7 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -1096,6 +1096,7 @@ class Router(Node):
"sharpd": 0,
"babeld": 0,
"pbrd": 0,
+ 'pathd': 0
}
self.daemons_options = {"zebra": ""}
self.reportCores = True
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index 3121551ee5..137a3d5a03 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -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"]:
@@ -727,6 +743,68 @@ 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)
+
else:
# Continuing in an existing context, add non-commented lines to it
current_context_lines.append(line)
@@ -1244,6 +1322,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 +1407,32 @@ 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
+
+ # 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 +1447,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 +1464,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 +1485,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 +1667,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 +1719,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 = []
diff --git a/tools/frr.in b/tools/frr.in
index b860797d5b..889c075f81 100755
--- a/tools/frr.in
+++ b/tools/frr.in
@@ -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"
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index c78132508f..93a63cd964 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -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,11 @@ 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)
+ && (tried > 0)) {
+ vtysh_execute("exit");
} else if (tried) {
vtysh_execute("end");
vtysh_execute("configure");
@@ -689,6 +695,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 +747,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 +825,23 @@ 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))
+ && (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);
+ 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 +1249,41 @@ 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 vrf_node = {
.name = "vrf",
.node = VRF_NODE,
@@ -1974,6 +2039,60 @@ 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;
+}
+#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 +2466,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 +4275,37 @@ 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);
+#endif /* HAVE_PATHD */
+
/* keychain */
install_node(&keychain_node);
install_element(CONFIG_NODE, &key_chain_cmd);
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index d2675a81b9..9683518b67 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -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
diff --git a/yang/frr-pathd.yang b/yang/frr-pathd.yang
new file mode 100644
index 0000000000..03f0d3b024
--- /dev/null
+++ b/yang/frr-pathd.yang
@@ -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";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/yang/subdir.am b/yang/subdir.am
index 5be93dc4e9..47fc508901 100644
--- a/yang/subdir.am
+++ b/yang/subdir.am
@@ -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