summaryrefslogtreecommitdiff
path: root/doc/developer
diff options
context:
space:
mode:
Diffstat (limited to 'doc/developer')
-rw-r--r--doc/developer/.readthedocs.yaml18
-rw-r--r--doc/developer/bmp.rst49
-rw-r--r--doc/developer/building-docker.rst50
-rw-r--r--doc/developer/building-frr-for-archlinux.rst8
-rw-r--r--doc/developer/building-frr-for-centos6.rst4
-rw-r--r--doc/developer/building-frr-for-centos7.rst2
-rw-r--r--doc/developer/building-frr-for-centos8.rst2
-rw-r--r--doc/developer/building-frr-for-debian12.rst119
-rw-r--r--doc/developer/building-frr-for-debian8.rst10
-rw-r--r--doc/developer/building-frr-for-debian9.rst4
-rw-r--r--doc/developer/building-frr-for-freebsd10.rst4
-rw-r--r--doc/developer/building-frr-for-freebsd11.rst4
-rw-r--r--doc/developer/building-frr-for-freebsd13.rst4
-rw-r--r--doc/developer/building-frr-for-freebsd14.rst122
-rw-r--r--doc/developer/building-frr-for-freebsd9.rst4
-rw-r--r--doc/developer/building-frr-for-netbsd6.rst4
-rw-r--r--doc/developer/building-frr-for-netbsd7.rst4
-rw-r--r--doc/developer/building-frr-for-openbsd6.rst4
-rw-r--r--doc/developer/building-frr-for-opensuse.rst4
-rw-r--r--doc/developer/building-frr-for-ubuntu1404.rst7
-rw-r--r--doc/developer/building-frr-for-ubuntu1604.rst6
-rw-r--r--doc/developer/building-frr-for-ubuntu1804.rst7
-rw-r--r--doc/developer/building-frr-for-ubuntu2004.rst31
-rw-r--r--doc/developer/building-frr-for-ubuntu2204.rst164
-rw-r--r--doc/developer/building-libyang.rst6
-rw-r--r--doc/developer/building.rst7
-rw-r--r--doc/developer/checkpatch.rst1242
-rw-r--r--doc/developer/cli.rst34
-rw-r--r--doc/developer/cross-compiling.rst4
-rw-r--r--doc/developer/cspf.rst163
-rw-r--r--doc/developer/frr-release-procedure.rst13
-rw-r--r--doc/developer/include-compile.rst5
-rw-r--r--doc/developer/index.rst3
-rw-r--r--doc/developer/link-state.rst2
-rw-r--r--doc/developer/logging.rst17
-rw-r--r--doc/developer/mgmtd-dev.rst383
-rw-r--r--doc/developer/northbound/advanced-topics.rst301
-rw-r--r--doc/developer/northbound/architecture.rst282
-rw-r--r--doc/developer/northbound/demos.rst10
-rw-r--r--doc/developer/northbound/images/arch-after.pngbin0 -> 18651 bytes
-rw-r--r--doc/developer/northbound/images/arch-before.pngbin0 -> 4360 bytes
-rw-r--r--doc/developer/northbound/images/ly-ctx.pngbin0 -> 7242 bytes
-rw-r--r--doc/developer/northbound/images/lyd-node.pngbin0 -> 21699 bytes
-rw-r--r--doc/developer/northbound/images/lys-node.pngbin0 -> 18018 bytes
-rw-r--r--doc/developer/northbound/images/nb-layer.pngbin0 -> 25388 bytes
-rw-r--r--doc/developer/northbound/images/transactions.pngbin0 -> 21532 bytes
-rw-r--r--doc/developer/northbound/links.rst228
-rw-r--r--doc/developer/northbound/northbound.rst21
-rw-r--r--doc/developer/northbound/operational-data-rpcs-and-notifications.rst565
-rw-r--r--doc/developer/northbound/plugins-sysrepo.rst193
-rw-r--r--doc/developer/northbound/ppr-basic-test-topology.rst1627
-rw-r--r--doc/developer/northbound/ppr-mpls-basic-test-topology.rst1986
-rw-r--r--doc/developer/northbound/retrofitting-configuration-commands.rst1928
-rw-r--r--doc/developer/northbound/transactional-cli.rst244
-rw-r--r--doc/developer/northbound/yang-module-translator.rst633
-rw-r--r--doc/developer/northbound/yang-tools.rst114
-rw-r--r--doc/developer/ospf.rst1
-rw-r--r--doc/developer/process-architecture.rst54
-rw-r--r--doc/developer/rcu.rst9
-rw-r--r--doc/developer/requirements.txt1
-rw-r--r--doc/developer/scripting.rst6
-rw-r--r--doc/developer/subdir.am20
-rw-r--r--doc/developer/topotests-markers.rst13
-rw-r--r--doc/developer/topotests.rst303
-rw-r--r--doc/developer/workflow.rst89
-rw-r--r--doc/developer/zebra.rst239
66 files changed, 10813 insertions, 568 deletions
diff --git a/doc/developer/.readthedocs.yaml b/doc/developer/.readthedocs.yaml
new file mode 100644
index 0000000000..90ee5c7677
--- /dev/null
+++ b/doc/developer/.readthedocs.yaml
@@ -0,0 +1,18 @@
+# Required
+version: 2
+
+# Set the version of Python and other tools you might need
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.11"
+ apt_packages:
+ - graphviz
+
+python:
+ install:
+ - requirements: doc/developer/requirements.txt
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+ configuration: doc/developer/conf.py
diff --git a/doc/developer/bmp.rst b/doc/developer/bmp.rst
new file mode 100644
index 0000000000..1c0e4b0454
--- /dev/null
+++ b/doc/developer/bmp.rst
@@ -0,0 +1,49 @@
+.. _bmp:
+
+***
+BMP
+***
+
+RFC 7854
+========
+Missing features (non exhaustive):
+ - Per-Peer Header
+
+ - Peer Type Flag
+ - Peer Distingsher
+
+ - Peer Up
+
+ - Reason codes (according to TODO comments in code)
+
+Peer Type Flag and Peer Distinguisher can be implemented easily using RFC 9069's base code.
+
+RFC 9069
+========
+Everything that isn't listed here is implemented and should be working.
+Missing features (should be exhaustive):
+
+- Per-Peer Header
+
+ - Timestamp
+
+ - set to 0
+ - value is now saved `struct bgp_path_info -> locrib_uptime`
+ - needs testing
+
+- Peer Up/Down
+
+ - VRF/Table Name TLV
+
+ - code for TLV exists
+ - need better RFC understanding
+
+- Peer Down Only
+
+ - Reason code (bc not supported in RFC 7854 either)
+
+- Statistics Report
+
+ - Stat Type = 8: (64-bit Gauge) Number of routes in Loc-RIB.
+ - Stat Type = 10: Number of routes in per-AFI/SAFI Loc-RIB. The value is
+ structured as: 2-byte AFI, 1-byte SAFI, followed by a 64-bit Gauge.
diff --git a/doc/developer/building-docker.rst b/doc/developer/building-docker.rst
index 4cf356049e..644e02bd6c 100644
--- a/doc/developer/building-docker.rst
+++ b/doc/developer/building-docker.rst
@@ -14,10 +14,10 @@ source-built FRR on the following base platforms:
The following platform images are used to support Travis CI and can also
be used to reproduce topotest failures when the docker host is Ubuntu
-(tested on 18.04 and 20.04):
+(tested on 20.04 and 22.04):
-* Ubuntu 18.04
* Ubuntu 20.04
+* Ubuntu 22.04
The following platform images may also be built, but these simply install a
binary package from an existing repository and do not perform source builds:
@@ -130,57 +130,75 @@ No script, multi-arch (ex. amd64, arm64)::
-Building Ubuntu 18.04 Image
+Building Ubuntu 20.04 Image
---------------------------
Build image (from project root directory)::
- docker build -t frr-ubuntu18:latest -f docker/ubuntu18-ci/Dockerfile .
+ docker build -t frr-ubuntu20:latest --build-arg=UBUNTU_VERSION=20.04 -f docker/ubuntu-ci/Dockerfile .
+
+Running Full Topotest::
+
+ docker run --init -it --privileged --name frr-ubuntu20 -v /lib/modules:/lib/modules \
+ frr-ubuntu20:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile'
+
+Extract results from the above run into `run-results` dir and analyze::
+
+ tests/topotests/analyze.py -C frr-ubuntu20 -Ar run-results
Start the container::
- docker run -d --privileged --name frr-ubuntu18 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu18:latest
+ docker run -d --init --privileged --name frr-ubuntu20 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu20:latest
Running a topotest (when the docker host is Ubuntu)::
- docker exec frr-ubuntu18 bash -c 'cd ~/frr/tests/topotests/ospf-topo1 ; sudo pytest test_ospf_topo1.py'
+ docker exec frr-ubuntu20 bash -c 'cd ~/frr/tests/topotests/ospf_topo1 ; sudo pytest test_ospf_topo1.py'
Starting an interactive bash session::
- docker exec -it frr-ubuntu18 bash
+ docker exec -it frr-ubuntu20 bash
Stopping an removing a container::
- docker stop frr-ubuntu18 ; docker rm frr-ubuntu18
+ docker stop frr-ubuntu20 ; docker rm frr-ubuntu20
Removing the built image::
- docker rmi frr-ubuntu18:latest
+ docker rmi frr-ubuntu20:latest
-Building Ubuntu 20.04 Image
+Building Ubuntu 22.04 Image
---------------------------
Build image (from project root directory)::
- docker build -t frr-ubuntu20:latest -f docker/ubuntu20-ci/Dockerfile .
+ docker build -t frr-ubuntu22:latest -f docker/ubuntu-ci/Dockerfile .
+
+Running Full Topotest::
+
+ docker run --init -it --privileged --name frr-ubuntu22 -v /lib/modules:/lib/modules \
+ frr-ubuntu22:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile'
+
+Extract results from the above run into `run-results` dir and analyze::
+
+ tests/topotests/analyze.py -C frr-ubuntu22 -Ar run-results
Start the container::
- docker run -d --privileged --name frr-ubuntu20 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu20:latest
+ docker run -d --init --privileged --name frr-ubuntu22 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu22:latest
Running a topotest (when the docker host is Ubuntu)::
- docker exec frr-ubuntu20 bash -c 'cd ~/frr/tests/topotests/ospf-topo1 ; sudo pytest test_ospf_topo1.py'
+ docker exec frr-ubuntu22 bash -c 'cd ~/frr/tests/topotests/ospf_topo1 ; sudo pytest test_ospf_topo1.py'
Starting an interactive bash session::
- docker exec -it frr-ubuntu20 bash
+ docker exec -it frr-ubuntu22 bash
Stopping an removing a container::
- docker stop frr-ubuntu20 ; docker rm frr-ubuntu20
+ docker stop frr-ubuntu22 ; docker rm frr-ubuntu22
Removing the built image::
- docker rmi frr-ubuntu20:latest
+ docker rmi frr-ubuntu22:latest
diff --git a/doc/developer/building-frr-for-archlinux.rst b/doc/developer/building-frr-for-archlinux.rst
index 406d22d618..8b0df217a0 100644
--- a/doc/developer/building-frr-for-archlinux.rst
+++ b/doc/developer/building-frr-for-archlinux.rst
@@ -11,18 +11,12 @@ Installing Dependencies
git autoconf automake libtool make cmake pcre readline texinfo \
pkg-config pam json-c bison flex python-pytest \
c-ares python python2-ipaddress python-sphinx \
- net-snmp perl libcap libelf libunwind
+ net-snmp perl libcap libelf libunwind protobuf-c
.. include:: building-libunwind-note.rst
.. include:: building-libyang.rst
-Protobuf
-^^^^^^^^
-
-.. code-block:: console
-
- sudo pacman -S protobuf-c
ZeroMQ
^^^^^^
diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst
index 233d089f79..3531162360 100644
--- a/doc/developer/building-frr-for-centos6.rst
+++ b/doc/developer/building-frr-for-centos6.rst
@@ -124,7 +124,7 @@ Install libyang and its dependencies:
sudo yum install pcre-devel doxygen cmake
git clone https://github.com/CESNET/libyang.git
cd libyang
- git checkout 090926a89d59a3c4000719505d563aaf6ac60f2
+ git checkout v2.1.128
mkdir build ; cd build
cmake -DENABLE_LYD_PRIV=ON -DCMAKE_INSTALL_PREFIX:PATH=/usr -D CMAKE_BUILD_TYPE:String="Release" ..
make build-rpm
@@ -161,10 +161,8 @@ an example.)
./configure \
--bindir=/usr/bin \
--sbindir=/usr/lib/frr \
- --sysconfdir=/etc/frr \
--libdir=/usr/lib/frr \
--libexecdir=/usr/lib/frr \
- --localstatedir=/var/run/frr \
--with-moduledir=/usr/lib/frr/modules \
--disable-pimd \
--enable-snmp=agentx \
diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst
index e6da830194..eabf515d2e 100644
--- a/doc/developer/building-frr-for-centos7.rst
+++ b/doc/developer/building-frr-for-centos7.rst
@@ -58,10 +58,8 @@ an example.)
./configure \
--bindir=/usr/bin \
--sbindir=/usr/lib/frr \
- --sysconfdir=/etc/frr \
--libdir=/usr/lib/frr \
--libexecdir=/usr/lib/frr \
- --localstatedir=/var/run/frr \
--with-moduledir=/usr/lib/frr/modules \
--enable-snmp=agentx \
--enable-multipath=64 \
diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst
index 6d18e7be93..2d514ead1e 100644
--- a/doc/developer/building-frr-for-centos8.rst
+++ b/doc/developer/building-frr-for-centos8.rst
@@ -52,10 +52,8 @@ an example.)
./configure \
--bindir=/usr/bin \
--sbindir=/usr/lib/frr \
- --sysconfdir=/etc/frr \
--libdir=/usr/lib/frr \
--libexecdir=/usr/lib/frr \
- --localstatedir=/var/run/frr \
--with-moduledir=/usr/lib/frr/modules \
--enable-snmp=agentx \
--enable-multipath=64 \
diff --git a/doc/developer/building-frr-for-debian12.rst b/doc/developer/building-frr-for-debian12.rst
new file mode 100644
index 0000000000..06bc18c252
--- /dev/null
+++ b/doc/developer/building-frr-for-debian12.rst
@@ -0,0 +1,119 @@
+Debian 12
+=========
+
+Install required packages
+-------------------------
+
+Add packages:
+
+::
+
+ sudo apt-get install git autoconf automake libtool make \
+ libprotobuf-c-dev protobuf-c-compiler build-essential \
+ python3-dev python3-pytest python3-sphinx libjson-c-dev \
+ libelf-dev libreadline-dev cmake libcap-dev bison flex \
+ pkg-config texinfo gdb libgrpc-dev python3-grpc-tools
+
+.. include:: building-libunwind-note.rst
+
+.. include:: building-libyang.rst
+
+Get FRR, compile it and install it (from Git)
+---------------------------------------------
+
+**This assumes you want to build and install FRR from source and not
+using any packages**
+
+Add frr groups and user
+^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ sudo addgroup --system --gid 92 frr
+ sudo addgroup --system --gid 85 frrvty
+ sudo adduser --system --ingroup frr --home /var/opt/frr/ \
+ --gecos "FRR suite" --shell /bin/false frr
+ sudo usermod -a -G frrvty frr
+
+Download Source, configure and compile it
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+(You may prefer different options on configure statement. These are just
+an example.)
+
+::
+
+ git clone https://github.com/frrouting/frr.git frr
+ cd frr
+ ./bootstrap.sh
+ ./configure \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
+ --sbindir=/usr/lib/frr \
+ --enable-multipath=64 \
+ --enable-user=frr \
+ --enable-group=frr \
+ --enable-vty-group=frrvty \
+ --enable-configfile-mask=0640 \
+ --enable-logfile-mask=0640 \
+ --enable-fpm \
+ --with-pkg-git-version \
+ --with-pkg-extra-version=-MyOwnFRRVersion
+ make
+ make check
+ sudo make install
+
+For more compile options, see ``./configure --help``
+
+Create empty FRR configuration files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ sudo install -m 640 -o frr -g frr /dev/null /etc/frr/frr.conf
+ sudo install -m 640 -o frr -g frr tools/etc/frr/daemons /etc/frr/daemons
+
+Edit ``/etc/frr/daemons`` and enable the FRR daemons for the protocols you need
+
+Enable IP & IPv6 forwarding
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
+other settings)
+
+::
+
+ # Uncomment the next line to enable packet forwarding for IPv4
+ net.ipv4.ip_forward=1
+
+ # Uncomment the next line to enable packet forwarding for IPv6
+ # Enabling this option disables Stateless Address Autoconfiguration
+ # based on Router Advertisements for this host
+ net.ipv6.conf.all.forwarding=1
+
+**Reboot** or use ``sysctl -p`` to apply the same config to the running
+system
+
+Troubleshooting
+---------------
+
+Shared library error
+^^^^^^^^^^^^^^^^^^^^
+
+If you try and start any of the frrouting daemons you may see the below
+error due to the frrouting shared library directory not being found:
+
+::
+
+ ./zebra: error while loading shared libraries: libfrr.so.0: cannot open
+ shared object file: No such file or directory
+
+The fix is to add the following line to /etc/ld.so.conf which will
+continue to reference the library directory after the system reboots. To
+load the library directory path immediately run the ldconfig command
+after adding the line to the file eg:
+
+::
+
+ echo include /usr/local/lib >> /etc/ld.so.conf
+ ldconfig
diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst
index 7071cb660d..fe4eeea601 100644
--- a/doc/developer/building-frr-for-debian8.rst
+++ b/doc/developer/building-frr-for-debian8.rst
@@ -57,9 +57,9 @@ an example.)
cd frr
./bootstrap.sh
./configure \
- --localstatedir=/var/run/frr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
--sbindir=/usr/lib/frr \
- --sysconfdir=/etc/frr \
--enable-multipath=64 \
--enable-user=frr \
--enable-group=frr \
@@ -118,9 +118,9 @@ Troubleshooting
The local state directory must exist and have the correct permissions
applied for the frrouting daemons to start. In the above ./configure
-example the local state directory is set to /var/run/frr
-(--localstatedir=/var/run/frr) Debian considers /var/run/frr to be
-temporary and this is removed after a reboot.
+example the local state directory is set to ``/var`` such that ``/var/run/frr``
+is used. Debian considers ``/var/run/frr`` to be temporary and this is removed
+after a reboot.
When using a different local state directory you need to create the new
directory and change the ownership to the frr user, for example:
diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst
index 1b2f1b933a..a590cf7c74 100644
--- a/doc/developer/building-frr-for-debian9.rst
+++ b/doc/developer/building-frr-for-debian9.rst
@@ -47,9 +47,9 @@ an example.)
cd frr
./bootstrap.sh
./configure \
- --localstatedir=/var/opt/frr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
--sbindir=/usr/lib/frr \
- --sysconfdir=/etc/frr \
--enable-multipath=64 \
--enable-user=frr \
--enable-group=frr \
diff --git a/doc/developer/building-frr-for-freebsd10.rst b/doc/developer/building-frr-for-freebsd10.rst
index 707f1e7033..beefb59a27 100644
--- a/doc/developer/building-frr-for-freebsd10.rst
+++ b/doc/developer/building-frr-for-freebsd10.rst
@@ -60,9 +60,9 @@ an example)
export LDFLAGS="-L/usr/local/lib"
export CPPFLAGS="-I/usr/local/include"
./configure \
- --sysconfdir=/usr/local/etc/frr \
+ --sysconfdir=/usr/local/etc \
+ --localstatedir=/var \
--enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
- --localstatedir=/var/run/frr \
--prefix=/usr/local \
--enable-multipath=64 \
--enable-user=frr \
diff --git a/doc/developer/building-frr-for-freebsd11.rst b/doc/developer/building-frr-for-freebsd11.rst
index af0b72b16d..7c8fb83cfa 100644
--- a/doc/developer/building-frr-for-freebsd11.rst
+++ b/doc/developer/building-frr-for-freebsd11.rst
@@ -65,9 +65,9 @@ an example)
setenv CPPFLAGS -I/usr/local/include
ln -s /usr/local/bin/sphinx-build-3.6 /usr/local/bin/sphinx-build
./configure \
- --sysconfdir=/usr/local/etc/frr \
+ --sysconfdir=/usr/local/etc \
+ --localstatedir=/var \
--enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
- --localstatedir=/var/run/frr \
--prefix=/usr/local \
--enable-multipath=64 \
--enable-user=frr \
diff --git a/doc/developer/building-frr-for-freebsd13.rst b/doc/developer/building-frr-for-freebsd13.rst
index 0bc8277930..86506a9dd9 100644
--- a/doc/developer/building-frr-for-freebsd13.rst
+++ b/doc/developer/building-frr-for-freebsd13.rst
@@ -52,9 +52,9 @@ an example)
./bootstrap.sh
export MAKE=gmake LDFLAGS=-L/usr/local/lib CPPFLAGS=-I/usr/local/include
./configure \
- --sysconfdir=/usr/local/etc/frr \
+ --sysconfdir=/usr/local/etc \
+ --localstatedir=/var \
--enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
- --localstatedir=/var/run/frr \
--prefix=/usr/local \
--enable-multipath=64 \
--enable-user=frr \
diff --git a/doc/developer/building-frr-for-freebsd14.rst b/doc/developer/building-frr-for-freebsd14.rst
new file mode 100644
index 0000000000..b3fd37aa10
--- /dev/null
+++ b/doc/developer/building-frr-for-freebsd14.rst
@@ -0,0 +1,122 @@
+FreeBSD 14
+==========
+
+FreeBSD 14 restrictions:
+------------------------
+
+- MPLS is not supported on ``FreeBSD``. MPLS requires a Linux Kernel
+ (4.5 or higher). LDP can be built, but may have limited use without
+ MPLS
+- PIM for IPv6 is not currently supported on ``FreeBSD``.
+
+Install required packages
+-------------------------
+
+Add packages: (Allow the install of the package management tool if this
+is first package install and asked)
+
+.. code-block:: shell
+
+ pkg install autoconf automake bison c-ares git gmake json-c libtool \
+ libunwind libyang2 pkgconf protobuf-c py39-pytest py39-sphinx texinfo
+
+.. include:: building-libunwind-note.rst
+
+Get FRR, compile it and install it (from Git)
+---------------------------------------------
+
+**This assumes you want to build and install FRR from source and not using any
+packages**
+
+Add frr group and user
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: shell
+
+ pw groupadd frr -g 101
+ pw groupadd frrvty -g 102
+ pw adduser frr -g 101 -u 101 -G 102 -c "FRR suite" \
+ -d /usr/local/etc/frr -s /usr/sbin/nologin
+
+
+Download Source, configure and compile it
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+(You may prefer different options on configure statement. These are just
+an example)
+
+.. code-block:: shell
+
+ git clone https://github.com/frrouting/frr.git frr
+ cd frr
+ ./bootstrap.sh
+ export MAKE=gmake LDFLAGS=-L/usr/local/lib CPPFLAGS=-I/usr/local/include
+ ./configure \
+ --sysconfdir=/usr/local/etc \
+ --localstatedir=/var \
+ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
+ --prefix=/usr/local \
+ --enable-multipath=64 \
+ --enable-user=frr \
+ --enable-group=frr \
+ --enable-vty-group=frrvty \
+ --enable-configfile-mask=0640 \
+ --enable-logfile-mask=0640 \
+ --enable-fpm \
+ --with-pkg-git-version \
+ --with-pkg-extra-version=-MyOwnFRRVersion
+ gmake
+ gmake check
+ sudo gmake install
+
+Create empty FRR configuration files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: shell
+
+ sudo mkdir /usr/local/etc/frr
+
+For integrated config file:
+
+.. code-block:: shell
+
+ sudo touch /usr/local/etc/frr/frr.conf
+
+For individual config files:
+
+.. note:: Integrated config is preferred to individual config.
+
+.. code-block:: shell
+
+ sudo touch /usr/local/etc/frr/babeld.conf
+ sudo touch /usr/local/etc/frr/bfdd.conf
+ sudo touch /usr/local/etc/frr/bgpd.conf
+ sudo touch /usr/local/etc/frr/eigrpd.conf
+ sudo touch /usr/local/etc/frr/isisd.conf
+ sudo touch /usr/local/etc/frr/ldpd.conf
+ sudo touch /usr/local/etc/frr/nhrpd.conf
+ sudo touch /usr/local/etc/frr/ospf6d.conf
+ sudo touch /usr/local/etc/frr/ospfd.conf
+ sudo touch /usr/local/etc/frr/pbrd.conf
+ sudo touch /usr/local/etc/frr/pimd.conf
+ sudo touch /usr/local/etc/frr/ripd.conf
+ sudo touch /usr/local/etc/frr/ripngd.conf
+ sudo touch /usr/local/etc/frr/staticd.conf
+ sudo touch /usr/local/etc/frr/zebra.conf
+ sudo chown -R frr:frr /usr/local/etc/frr/
+ sudo touch /usr/local/etc/frr/vtysh.conf
+ sudo chown frr:frrvty /usr/local/etc/frr/vtysh.conf
+ sudo chmod 640 /usr/local/etc/frr/*.conf
+
+Enable IP & IPv6 forwarding
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Add the following lines to the end of ``/etc/sysctl.conf``:
+
+::
+
+ # Routing: We need to forward packets
+ net.inet.ip.forwarding=1
+ net.inet6.ip6.forwarding=1
+
+**Reboot** or use ``sysctl`` to apply the same config to the running system.
diff --git a/doc/developer/building-frr-for-freebsd9.rst b/doc/developer/building-frr-for-freebsd9.rst
index 30332875a0..9f9073d4e2 100644
--- a/doc/developer/building-frr-for-freebsd9.rst
+++ b/doc/developer/building-frr-for-freebsd9.rst
@@ -70,9 +70,9 @@ an example)
export LDFLAGS="-L/usr/local/lib"
export CPPFLAGS="-I/usr/local/include"
./configure \
- --sysconfdir=/usr/local/etc/frr \
+ --sysconfdir=/usr/local/etc \
+ --localstatedir=/var \
--enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
- --localstatedir=/var/run/frr \
--prefix=/usr/local \
--enable-multipath=64 \
--enable-user=frr \
diff --git a/doc/developer/building-frr-for-netbsd6.rst b/doc/developer/building-frr-for-netbsd6.rst
index 8958862fea..77c0e008ef 100644
--- a/doc/developer/building-frr-for-netbsd6.rst
+++ b/doc/developer/building-frr-for-netbsd6.rst
@@ -64,9 +64,9 @@ an example)
export LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib"
export CPPFLAGS="-I/usr/pkg/include"
./configure \
- --sysconfdir=/usr/pkg/etc/frr \
+ --sysconfdir=/usr/pkg/etc \
+ --localstatedir=/var \
--enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
- --localstatedir=/var/run/frr \
--enable-multipath=64 \
--enable-user=frr \
--enable-group=frr \
diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst
index e751ba338c..abb04a028b 100644
--- a/doc/developer/building-frr-for-netbsd7.rst
+++ b/doc/developer/building-frr-for-netbsd7.rst
@@ -55,9 +55,9 @@ an example)
export LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib"
export CPPFLAGS="-I/usr/pkg/include"
./configure \
- --sysconfdir=/usr/pkg/etc/frr \
+ --sysconfdir=/usr/pkg/etc \
+ --localstatedir=/var \
--enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
- --localstatedir=/var/run/frr \
--enable-multipath=64 \
--enable-user=frr \
--enable-group=frr \
diff --git a/doc/developer/building-frr-for-openbsd6.rst b/doc/developer/building-frr-for-openbsd6.rst
index 00bc2e5f09..6d7f346231 100644
--- a/doc/developer/building-frr-for-openbsd6.rst
+++ b/doc/developer/building-frr-for-openbsd6.rst
@@ -71,8 +71,8 @@ an example)
export LDFLAGS="-L/usr/local/lib"
export CPPFLAGS="-I/usr/local/include"
./configure \
- --sysconfdir=/etc/frr \
- --localstatedir=/var/frr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
--enable-multipath=64 \
--enable-user=_frr \
--enable-group=_frr \
diff --git a/doc/developer/building-frr-for-opensuse.rst b/doc/developer/building-frr-for-opensuse.rst
index 3ff445bcd0..6e9913de48 100644
--- a/doc/developer/building-frr-for-opensuse.rst
+++ b/doc/developer/building-frr-for-opensuse.rst
@@ -13,11 +13,13 @@ Installing Dependencies
zypper in git autoconf automake libtool make \
readline-devel texinfo net-snmp-devel groff pkgconfig libjson-c-devel\
pam-devel python3-pytest bison flex c-ares-devel python3-devel\
- python3-Sphinx perl patch libcap-devel libyang-devel \
+ python3-Sphinx perl patch libcap-devel \
libelf-devel libunwind-devel protobuf-c
.. include:: building-libunwind-note.rst
+.. include:: building-libyang.rst
+
Building & Installing FRR
-------------------------
diff --git a/doc/developer/building-frr-for-ubuntu1404.rst b/doc/developer/building-frr-for-ubuntu1404.rst
index cc6c3c03f3..dd3f98a58e 100644
--- a/doc/developer/building-frr-for-ubuntu1404.rst
+++ b/doc/developer/building-frr-for-ubuntu1404.rst
@@ -14,16 +14,11 @@ Installing Dependencies
git autoconf automake libtool make libreadline-dev texinfo \
pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \
libc-ares-dev python3-dev python3-sphinx install-info build-essential \
+ protobuf-c-compiler libprotobuf-c-dev \
libsnmp-dev perl libcap-dev libelf-dev
.. include:: building-libyang.rst
-Protobuf
-^^^^^^^^
-
-.. code-block:: console
-
- sudo apt-get install protobuf-c-compiler libprotobuf-c-dev
Building & Installing FRR
-------------------------
diff --git a/doc/developer/building-frr-for-ubuntu1604.rst b/doc/developer/building-frr-for-ubuntu1604.rst
index e5c2389f39..f3b6aa0de9 100644
--- a/doc/developer/building-frr-for-ubuntu1604.rst
+++ b/doc/developer/building-frr-for-ubuntu1604.rst
@@ -19,12 +19,6 @@ Installing Dependencies
.. include:: building-libyang.rst
-Protobuf
-^^^^^^^^
-
-.. code-block:: console
-
- sudo apt-get install protobuf-c-compiler libprotobuf-c-dev
Building & Installing FRR
-------------------------
diff --git a/doc/developer/building-frr-for-ubuntu1804.rst b/doc/developer/building-frr-for-ubuntu1804.rst
index fcfd94ec2c..b4880e26be 100644
--- a/doc/developer/building-frr-for-ubuntu1804.rst
+++ b/doc/developer/building-frr-for-ubuntu1804.rst
@@ -15,18 +15,13 @@ Installing Dependencies
pkg-config libpam0g-dev libjson-c-dev bison flex \
libc-ares-dev python3-dev python3-sphinx \
install-info build-essential libsnmp-dev perl libcap-dev \
+ protobuf-c-compiler libprotobuf-c-dev \
libelf-dev libunwind-dev
.. include:: building-libunwind-note.rst
.. include:: building-libyang.rst
-Protobuf
-^^^^^^^^
-
-.. code-block:: console
-
- sudo apt-get install protobuf-c-compiler libprotobuf-c-dev
ZeroMQ
^^^^^^
diff --git a/doc/developer/building-frr-for-ubuntu2004.rst b/doc/developer/building-frr-for-ubuntu2004.rst
index fdfc25da9d..3db97c4b2d 100644
--- a/doc/developer/building-frr-for-ubuntu2004.rst
+++ b/doc/developer/building-frr-for-ubuntu2004.rst
@@ -15,34 +15,33 @@ Installing Dependencies
pkg-config libpam0g-dev libjson-c-dev bison flex \
libc-ares-dev python3-dev python3-sphinx \
install-info build-essential libsnmp-dev perl \
- libcap-dev python2 libelf-dev libunwind-dev
+ protobuf-c-compiler libprotobuf-c-dev \
+ libcap-dev libelf-dev libunwind-dev
.. include:: building-libunwind-note.rst
-Note that Ubuntu 20 no longer installs python 2.x, so it must be
-installed explicitly. Ensure that your system has a symlink named
-``/usr/bin/python`` pointing at ``/usr/bin/python3``.
+.. include:: building-libyang.rst
-In addition, ``pip`` for python2 must be installed if you wish to run
-the FRR topotests. That version of ``pip`` is not available from the
-ubuntu apt repositories; in order to install it:
+GRPC
+^^^^
+If GRPC is enabled using ``--enable-grpc`` the following packages should be
+installed.
-.. code-block:: shell
+.. code-block:: console
- curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
- sudo python2 ./get-pip.py
+ sudo apt-get install libgrpc++-dev protobuf-compiler-grpc
- # And verify the installation
- pip2 --version
-.. include:: building-libyang.rst
+Config Rollbacks
+^^^^^^^^^^^^^^^^
-Protobuf
-^^^^^^^^
+If config rollbacks are enabled using ``--enable-config-rollbacks``
+the sqlite3 developer package also should be installed.
.. code-block:: console
- sudo apt-get install protobuf-c-compiler libprotobuf-c-dev
+ sudo apt install libsqlite3-dev
+
ZeroMQ
^^^^^^
diff --git a/doc/developer/building-frr-for-ubuntu2204.rst b/doc/developer/building-frr-for-ubuntu2204.rst
new file mode 100644
index 0000000000..c898c3cd2c
--- /dev/null
+++ b/doc/developer/building-frr-for-ubuntu2204.rst
@@ -0,0 +1,164 @@
+Ubuntu 22.04 LTS
+================
+
+This document describes installation from source. If you want to build a
+``deb``, see :ref:`packaging-debian`.
+
+Installing Dependencies
+-----------------------
+
+.. code-block:: console
+
+ sudo apt update
+ sudo apt-get install \
+ git autoconf automake libtool make libreadline-dev texinfo \
+ pkg-config libpam0g-dev libjson-c-dev bison flex \
+ libc-ares-dev python3-dev python3-sphinx \
+ install-info build-essential libsnmp-dev perl \
+ libcap-dev libelf-dev libunwind-dev \
+ protobuf-c-compiler libprotobuf-c-dev
+
+.. include:: building-libunwind-note.rst
+
+.. include:: building-libyang.rst
+
+GRPC
+^^^^
+If GRPC is enabled using ``--enable-grpc`` the following packages should be
+installed.
+
+.. code-block:: console
+
+ sudo apt-get install libgrpc++-dev protobuf-compiler-grpc
+
+
+Config Rollbacks
+^^^^^^^^^^^^^^^^
+
+If config rollbacks are enabled using ``--enable-config-rollbacks``
+the sqlite3 developer package also should be installed.
+
+.. code-block:: console
+
+ sudo apt install libsqlite3-dev
+
+
+ZeroMQ
+^^^^^^
+This is optional
+
+.. code-block:: console
+
+ sudo apt-get install libzmq5 libzmq3-dev
+
+Building & Installing FRR
+-------------------------
+
+Add FRR user and groups
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+ sudo groupadd -r -g 92 frr
+ sudo groupadd -r -g 85 frrvty
+ sudo adduser --system --ingroup frr --home /var/run/frr/ \
+ --gecos "FRR suite" --shell /sbin/nologin frr
+ sudo usermod -a -G frrvty frr
+
+Compile
+^^^^^^^
+
+.. include:: include-compile.rst
+
+Install FRR configuration files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+ sudo install -m 775 -o frr -g frr -d /var/log/frr
+ sudo install -m 775 -o frr -g frrvty -d /etc/frr
+ sudo install -m 640 -o frr -g frrvty tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
+ sudo install -m 640 -o frr -g frr tools/etc/frr/frr.conf /etc/frr/frr.conf
+ sudo install -m 640 -o frr -g frr tools/etc/frr/daemons.conf /etc/frr/daemons.conf
+ sudo install -m 640 -o frr -g frr tools/etc/frr/daemons /etc/frr/daemons
+
+Tweak sysctls
+^^^^^^^^^^^^^
+
+Some sysctls need to be changed in order to enable IPv4/IPv6 forwarding and
+MPLS (if supported by your platform). If your platform does not support MPLS,
+skip the MPLS related configuration in this section.
+
+Edit :file:`/etc/sysctl.conf` and uncomment the following values (ignore the
+other settings):
+
+::
+
+ # Uncomment the next line to enable packet forwarding for IPv4
+ net.ipv4.ip_forward=1
+
+ # Uncomment the next line to enable packet forwarding for IPv6
+ # Enabling this option disables Stateless Address Autoconfiguration
+ # based on Router Advertisements for this host
+ net.ipv6.conf.all.forwarding=1
+
+Reboot or use ``sysctl -p`` to apply the same config to the running system.
+
+Add MPLS kernel modules
+"""""""""""""""""""""""
+
+Ubuntu 20.04 ships with kernel 5.4; MPLS modules are present by default. To
+enable, add the following lines to :file:`/etc/modules-load.d/modules.conf`:
+
+::
+
+ # Load MPLS Kernel Modules
+ mpls_router
+ mpls_iptunnel
+
+
+And load the kernel modules on the running system:
+
+.. code-block:: console
+
+ sudo modprobe mpls-router mpls-iptunnel
+
+If the above command returns an error, you may need to install the appropriate
+or latest linux-modules-extra-<kernel-version>-generic package. For example
+``apt-get install linux-modules-extra-`uname -r`-generic``
+
+Enable MPLS Forwarding
+""""""""""""""""""""""
+
+Edit :file:`/etc/sysctl.conf` and the following lines. Make sure to add a line
+equal to :file:`net.mpls.conf.eth0.input` for each interface used with MPLS.
+
+::
+
+ # Enable MPLS Label processing on all interfaces
+ net.mpls.conf.eth0.input=1
+ net.mpls.conf.eth1.input=1
+ net.mpls.conf.eth2.input=1
+ net.mpls.platform_labels=100000
+
+Install service files
+^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+ sudo install -m 644 tools/frr.service /etc/systemd/system/frr.service
+ sudo systemctl enable frr
+
+Enable daemons
+^^^^^^^^^^^^^^
+
+Open :file:`/etc/frr/daemons` with your text editor of choice. Look for the
+section with ``watchfrr_enable=...`` and ``zebra=...`` etc. Enable the daemons
+as required by changing the value to ``yes``.
+
+Start FRR
+^^^^^^^^^
+
+.. code-block:: shell
+
+ systemctl start frr
diff --git a/doc/developer/building-libyang.rst b/doc/developer/building-libyang.rst
index c36cd34287..a46c79376c 100644
--- a/doc/developer/building-libyang.rst
+++ b/doc/developer/building-libyang.rst
@@ -10,11 +10,11 @@ The FRR project builds some binary ``libyang`` packages.
RPM packages are at our `RPM repository <https://rpm.frrouting.org>`_.
DEB packages are available as CI artifacts `here
-<https://ci1.netdef.org/browse/LIBYANG-LIBYANGV2/latestSuccessful/artifact>`_.
+<https://ci1.netdef.org/browse/LIBYANG-LIBYANG21/latestSuccessful/artifact>`_.
.. warning::
- ``libyang`` version 2.0.0 or newer is required to build FRR.
+ ``libyang`` version 2.1.128 or newer is required to build FRR.
.. note::
@@ -39,7 +39,7 @@ DEB packages are available as CI artifacts `here
git clone https://github.com/CESNET/libyang.git
cd libyang
- git checkout v2.0.0
+ git checkout v2.1.128
mkdir build; cd build
cmake -D CMAKE_INSTALL_PREFIX:PATH=/usr \
-D CMAKE_BUILD_TYPE:String="Release" ..
diff --git a/doc/developer/building.rst b/doc/developer/building.rst
index 2d8cc209b0..6762604f32 100644
--- a/doc/developer/building.rst
+++ b/doc/developer/building.rst
@@ -9,25 +9,28 @@ Building FRR
static-linking
building-frr-for-alpine
+ building-frr-for-archlinux
building-frr-for-centos6
building-frr-for-centos7
building-frr-for-centos8
building-frr-for-debian8
building-frr-for-debian9
+ building-frr-for-debian12
building-frr-for-fedora
- building-frr-for-opensuse
building-frr-for-freebsd9
building-frr-for-freebsd10
building-frr-for-freebsd11
building-frr-for-freebsd13
+ building-frr-for-freebsd14
building-frr-for-netbsd6
building-frr-for-netbsd7
building-frr-for-openbsd6
+ building-frr-for-opensuse
building-frr-for-openwrt
building-frr-for-ubuntu1404
building-frr-for-ubuntu1604
building-frr-for-ubuntu1804
building-frr-for-ubuntu2004
- building-frr-for-archlinux
+ building-frr-for-ubuntu2204
building-docker
cross-compiling
diff --git a/doc/developer/checkpatch.rst b/doc/developer/checkpatch.rst
new file mode 100644
index 0000000000..4ef261ba22
--- /dev/null
+++ b/doc/developer/checkpatch.rst
@@ -0,0 +1,1242 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+.. _checkpatch:
+
+==========
+Checkpatch
+==========
+
+Checkpatch (scripts/checkpatch.pl) is a perl script which checks for trivial
+style violations in patches and optionally corrects them. Checkpatch can
+also be run on file contexts and without the kernel tree.
+
+Checkpatch is not always right. Your judgement takes precedence over checkpatch
+messages. If your code looks better with the violations, then its probably
+best left alone.
+
+
+Options
+=======
+
+This section will describe the options checkpatch can be run with.
+
+Usage::
+
+ ./scripts/checkpatch.pl [OPTION]... [FILE]...
+
+Available options:
+
+ - -q, --quiet
+
+ Enable quiet mode.
+
+ - -v, --verbose
+ Enable verbose mode. Additional verbose test descriptions are output
+ so as to provide information on why that particular message is shown.
+
+ - --no-tree
+
+ Run checkpatch without the kernel tree.
+
+ - --no-signoff
+
+ Disable the 'Signed-off-by' line check. The sign-off is a simple line at
+ the end of the explanation for the patch, which certifies that you wrote it
+ or otherwise have the right to pass it on as an open-source patch.
+
+ Example::
+
+ Signed-off-by: Random J Developer <random@developer.example.org>
+
+ Setting this flag effectively stops a message for a missing signed-off-by
+ line in a patch context.
+
+ - --patch
+
+ Treat FILE as a patch. This is the default option and need not be
+ explicitly specified.
+
+ - --emacs
+
+ Set output to emacs compile window format. This allows emacs users to jump
+ from the error in the compile window directly to the offending line in the
+ patch.
+
+ - --terse
+
+ Output only one line per report.
+
+ - --showfile
+
+ Show the diffed file position instead of the input file position.
+
+ - -g, --git
+
+ Treat FILE as a single commit or a git revision range.
+
+ Single commit with:
+
+ - <rev>
+ - <rev>^
+ - <rev>~n
+
+ Multiple commits with:
+
+ - <rev1>..<rev2>
+ - <rev1>...<rev2>
+ - <rev>-<count>
+
+ - -f, --file
+
+ Treat FILE as a regular source file. This option must be used when running
+ checkpatch on source files in the kernel.
+
+ - --subjective, --strict
+
+ Enable stricter tests in checkpatch. By default the tests emitted as CHECK
+ do not activate by default. Use this flag to activate the CHECK tests.
+
+ - --list-types
+
+ Every message emitted by checkpatch has an associated TYPE. Add this flag
+ to display all the types in checkpatch.
+
+ Note that when this flag is active, checkpatch does not read the input FILE,
+ and no message is emitted. Only a list of types in checkpatch is output.
+
+ - --types TYPE(,TYPE2...)
+
+ Only display messages with the given types.
+
+ Example::
+
+ ./scripts/checkpatch.pl mypatch.patch --types EMAIL_SUBJECT,BRACES
+
+ - --ignore TYPE(,TYPE2...)
+
+ Checkpatch will not emit messages for the specified types.
+
+ Example::
+
+ ./scripts/checkpatch.pl mypatch.patch --ignore EMAIL_SUBJECT,BRACES
+
+ - --show-types
+
+ By default checkpatch doesn't display the type associated with the messages.
+ Set this flag to show the message type in the output.
+
+ - --max-line-length=n
+
+ Set the max line length (default 100). If a line exceeds the specified
+ length, a LONG_LINE message is emitted.
+
+
+ The message level is different for patch and file contexts. For patches,
+ a WARNING is emitted. While a milder CHECK is emitted for files. So for
+ file contexts, the --strict flag must also be enabled.
+
+ - --min-conf-desc-length=n
+
+ Set the Kconfig entry minimum description length, if shorter, warn.
+
+ - --tab-size=n
+
+ Set the number of spaces for tab (default 8).
+
+ - --root=PATH
+
+ PATH to the kernel tree root.
+
+ This option must be specified when invoking checkpatch from outside
+ the kernel root.
+
+ - --no-summary
+
+ Suppress the per file summary.
+
+ - --mailback
+
+ Only produce a report in case of Warnings or Errors. Milder Checks are
+ excluded from this.
+
+ - --summary-file
+
+ Include the filename in summary.
+
+ - --debug KEY=[0|1]
+
+ Turn on/off debugging of KEY, where KEY is one of 'values', 'possible',
+ 'type', and 'attr' (default is all off).
+
+ - --fix
+
+ This is an EXPERIMENTAL feature. If correctable errors exists, a file
+ <inputfile>.EXPERIMENTAL-checkpatch-fixes is created which has the
+ automatically fixable errors corrected.
+
+ - --fix-inplace
+
+ EXPERIMENTAL - Similar to --fix but input file is overwritten with fixes.
+
+ DO NOT USE this flag unless you are absolutely sure and you have a backup
+ in place.
+
+ - --ignore-perl-version
+
+ Override checking of perl version. Runtime errors maybe encountered after
+ enabling this flag if the perl version does not meet the minimum specified.
+
+ - --codespell
+
+ Use the codespell dictionary for checking spelling errors.
+
+ - --codespellfile
+
+ Use the specified codespell file.
+ Default is '/usr/share/codespell/dictionary.txt'.
+
+ - --typedefsfile
+
+ Read additional types from this file.
+
+ - --color[=WHEN]
+
+ Use colors 'always', 'never', or only when output is a terminal ('auto').
+ Default is 'auto'.
+
+ - --kconfig-prefix=WORD
+
+ Use WORD as a prefix for Kconfig symbols (default is `CONFIG_`).
+
+ - -h, --help, --version
+
+ Display the help text.
+
+Message Levels
+==============
+
+Messages in checkpatch are divided into three levels. The levels of messages
+in checkpatch denote the severity of the error. They are:
+
+ - ERROR
+
+ This is the most strict level. Messages of type ERROR must be taken
+ seriously as they denote things that are very likely to be wrong.
+
+ - WARNING
+
+ This is the next stricter level. Messages of type WARNING requires a
+ more careful review. But it is milder than an ERROR.
+
+ - CHECK
+
+ This is the mildest level. These are things which may require some thought.
+
+Type Descriptions
+=================
+
+This section contains a description of all the message types in checkpatch.
+
+.. Types in this section are also parsed by checkpatch.
+.. The types are grouped into subsections based on use.
+
+
+Allocation style
+----------------
+
+ **ALLOC_ARRAY_ARGS**
+ The first argument for kcalloc or kmalloc_array should be the
+ number of elements. sizeof() as the first argument is generally
+ wrong.
+
+ See: https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
+
+ **ALLOC_SIZEOF_STRUCT**
+ The allocation style is bad. In general for family of
+ allocation functions using sizeof() to get memory size,
+ constructs like::
+
+ p = alloc(sizeof(struct foo), ...)
+
+ should be::
+
+ p = alloc(sizeof(*p), ...)
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#allocating-memory
+
+ **ALLOC_WITH_MULTIPLY**
+ Prefer kmalloc_array/kcalloc over kmalloc/kzalloc with a
+ sizeof multiply.
+
+ See: https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
+
+
+API usage
+---------
+
+ **ARCH_DEFINES**
+ Architecture specific defines should be avoided wherever
+ possible.
+
+ **ARCH_INCLUDE_LINUX**
+ Whenever asm/file.h is included and linux/file.h exists, a
+ conversion can be made when linux/file.h includes asm/file.h.
+ However this is not always the case (See signal.h).
+ This message type is emitted only for includes from arch/.
+
+ **AVOID_BUG**
+ BUG() or BUG_ON() should be avoided totally.
+ Use WARN() and WARN_ON() instead, and handle the "impossible"
+ error condition as gracefully as possible.
+
+ See: https://www.kernel.org/doc/html/latest/process/deprecated.html#bug-and-bug-on
+
+ **CONSIDER_KSTRTO**
+ The simple_strtol(), simple_strtoll(), simple_strtoul(), and
+ simple_strtoull() functions explicitly ignore overflows, which
+ may lead to unexpected results in callers. The respective kstrtol(),
+ kstrtoll(), kstrtoul(), and kstrtoull() functions tend to be the
+ correct replacements.
+
+ See: https://www.kernel.org/doc/html/latest/process/deprecated.html#simple-strtol-simple-strtoll-simple-strtoul-simple-strtoull
+
+ **CONSTANT_CONVERSION**
+ Use of __constant_<foo> form is discouraged for the following functions::
+
+ __constant_cpu_to_be[x]
+ __constant_cpu_to_le[x]
+ __constant_be[x]_to_cpu
+ __constant_le[x]_to_cpu
+ __constant_htons
+ __constant_ntohs
+
+ Using any of these outside of include/uapi/ is not preferred as using the
+ function without __constant_ is identical when the argument is a
+ constant.
+
+ In big endian systems, the macros like __constant_cpu_to_be32(x) and
+ cpu_to_be32(x) expand to the same expression::
+
+ #define __constant_cpu_to_be32(x) ((__force __be32)(__u32)(x))
+ #define __cpu_to_be32(x) ((__force __be32)(__u32)(x))
+
+ In little endian systems, the macros __constant_cpu_to_be32(x) and
+ cpu_to_be32(x) expand to __constant_swab32 and __swab32. __swab32
+ has a __builtin_constant_p check::
+
+ #define __swab32(x) \
+ (__builtin_constant_p((__u32)(x)) ? \
+ ___constant_swab32(x) : \
+ __fswab32(x))
+
+ So ultimately they have a special case for constants.
+ Similar is the case with all of the macros in the list. Thus
+ using the __constant_... forms are unnecessarily verbose and
+ not preferred outside of include/uapi.
+
+ See: https://lore.kernel.org/lkml/1400106425.12666.6.camel@joe-AO725/
+
+ **DEPRECATED_API**
+ Usage of a deprecated RCU API is detected. It is recommended to replace
+ old flavourful RCU APIs by their new vanilla-RCU counterparts.
+
+ The full list of available RCU APIs can be viewed from the kernel docs.
+
+ See: https://www.kernel.org/doc/html/latest/RCU/whatisRCU.html#full-list-of-rcu-apis
+
+ **DEPRECATED_VARIABLE**
+ EXTRA_{A,C,CPP,LD}FLAGS are deprecated and should be replaced by the new
+ flags added via commit f77bf01425b1 ("kbuild: introduce ccflags-y,
+ asflags-y and ldflags-y").
+
+ The following conversion scheme maybe used::
+
+ EXTRA_AFLAGS -> asflags-y
+ EXTRA_CFLAGS -> ccflags-y
+ EXTRA_CPPFLAGS -> cppflags-y
+ EXTRA_LDFLAGS -> ldflags-y
+
+ See:
+
+ 1. https://lore.kernel.org/lkml/20070930191054.GA15876@uranus.ravnborg.org/
+ 2. https://lore.kernel.org/lkml/1313384834-24433-12-git-send-email-lacombar@gmail.com/
+ 3. https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
+
+ **DEVICE_ATTR_FUNCTIONS**
+ The function names used in DEVICE_ATTR is unusual.
+ Typically, the store and show functions are used with <attr>_store and
+ <attr>_show, where <attr> is a named attribute variable of the device.
+
+ Consider the following examples::
+
+ static DEVICE_ATTR(type, 0444, type_show, NULL);
+ static DEVICE_ATTR(power, 0644, power_show, power_store);
+
+ The function names should preferably follow the above pattern.
+
+ See: https://www.kernel.org/doc/html/latest/driver-api/driver-model/device.html#attributes
+
+ **DEVICE_ATTR_RO**
+ The DEVICE_ATTR_RO(name) helper macro can be used instead of
+ DEVICE_ATTR(name, 0444, name_show, NULL);
+
+ Note that the macro automatically appends _show to the named
+ attribute variable of the device for the show method.
+
+ See: https://www.kernel.org/doc/html/latest/driver-api/driver-model/device.html#attributes
+
+ **DEVICE_ATTR_RW**
+ The DEVICE_ATTR_RW(name) helper macro can be used instead of
+ DEVICE_ATTR(name, 0644, name_show, name_store);
+
+ Note that the macro automatically appends _show and _store to the
+ named attribute variable of the device for the show and store methods.
+
+ See: https://www.kernel.org/doc/html/latest/driver-api/driver-model/device.html#attributes
+
+ **DEVICE_ATTR_WO**
+ The DEVICE_AATR_WO(name) helper macro can be used instead of
+ DEVICE_ATTR(name, 0200, NULL, name_store);
+
+ Note that the macro automatically appends _store to the
+ named attribute variable of the device for the store method.
+
+ See: https://www.kernel.org/doc/html/latest/driver-api/driver-model/device.html#attributes
+
+ **DUPLICATED_SYSCTL_CONST**
+ Commit d91bff3011cf ("proc/sysctl: add shared variables for range
+ check") added some shared const variables to be used instead of a local
+ copy in each source file.
+
+ Consider replacing the sysctl range checking value with the shared
+ one in include/linux/sysctl.h. The following conversion scheme may
+ be used::
+
+ &zero -> SYSCTL_ZERO
+ &one -> SYSCTL_ONE
+ &int_max -> SYSCTL_INT_MAX
+
+ See:
+
+ 1. https://lore.kernel.org/lkml/20190430180111.10688-1-mcroce@redhat.com/
+ 2. https://lore.kernel.org/lkml/20190531131422.14970-1-mcroce@redhat.com/
+
+ **ENOSYS**
+ ENOSYS means that a nonexistent system call was called.
+ Earlier, it was wrongly used for things like invalid operations on
+ otherwise valid syscalls. This should be avoided in new code.
+
+ See: https://lore.kernel.org/lkml/5eb299021dec23c1a48fa7d9f2c8b794e967766d.1408730669.git.luto@amacapital.net/
+
+ **ENOTSUPP**
+ ENOTSUPP is not a standard error code and should be avoided in new patches.
+ EOPNOTSUPP should be used instead.
+
+ See: https://lore.kernel.org/netdev/20200510182252.GA411829@lunn.ch/
+
+ **EXPORT_SYMBOL**
+ EXPORT_SYMBOL should immediately follow the symbol to be exported.
+
+ **IN_ATOMIC**
+ in_atomic() is not for driver use so any such use is reported as an ERROR.
+ Also in_atomic() is often used to determine if sleeping is permitted,
+ but it is not reliable in this use model. Therefore its use is
+ strongly discouraged.
+
+ However, in_atomic() is ok for core kernel use.
+
+ See: https://lore.kernel.org/lkml/20080320201723.b87b3732.akpm@linux-foundation.org/
+
+ **LOCKDEP**
+ The lockdep_no_validate class was added as a temporary measure to
+ prevent warnings on conversion of device->sem to device->mutex.
+ It should not be used for any other purpose.
+
+ See: https://lore.kernel.org/lkml/1268959062.9440.467.camel@laptop/
+
+ **MALFORMED_INCLUDE**
+ The #include statement has a malformed path. This has happened
+ because the author has included a double slash "//" in the pathname
+ accidentally.
+
+ **USE_LOCKDEP**
+ lockdep_assert_held() annotations should be preferred over
+ assertions based on spin_is_locked()
+
+ See: https://www.kernel.org/doc/html/latest/locking/lockdep-design.html#annotations
+
+ **UAPI_INCLUDE**
+ No #include statements in include/uapi should use a uapi/ path.
+
+ **USLEEP_RANGE**
+ usleep_range() should be preferred over udelay(). The proper way of
+ using usleep_range() is mentioned in the kernel docs.
+
+ See: https://www.kernel.org/doc/html/latest/timers/timers-howto.html#delays-information-on-the-various-kernel-delay-sleep-mechanisms
+
+
+Comments
+--------
+
+ **BLOCK_COMMENT_STYLE**
+ The comment style is incorrect. The preferred style for multi-
+ line comments is::
+
+ /*
+ * This is the preferred style
+ * for multi line comments.
+ */
+
+ The networking comment style is a bit different, with the first line
+ not empty like the former::
+
+ /* This is the preferred comment style
+ * for files in net/ and drivers/net/
+ */
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#commenting
+
+ **C99_COMMENTS**
+ C99 style single line comments (//) should not be used.
+ Prefer the block comment style instead.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#commenting
+
+ **DATA_RACE**
+ Applications of data_race() should have a comment so as to document the
+ reasoning behind why it was deemed safe.
+
+ See: https://lore.kernel.org/lkml/20200401101714.44781-1-elver@google.com/
+
+ **FSF_MAILING_ADDRESS**
+ Kernel maintainers reject new instances of the GPL boilerplate paragraph
+ directing people to write to the FSF for a copy of the GPL, since the
+ FSF has moved in the past and may do so again.
+ So do not write paragraphs about writing to the Free Software Foundation's
+ mailing address.
+
+ See: https://lore.kernel.org/lkml/20131006222342.GT19510@leaf/
+
+
+Commit message
+--------------
+
+ **BAD_SIGN_OFF**
+ The signed-off-by line does not fall in line with the standards
+ specified by the community.
+
+ See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#developer-s-certificate-of-origin-1-1
+
+ **BAD_STABLE_ADDRESS_STYLE**
+ The email format for stable is incorrect.
+ Some valid options for stable address are::
+
+ 1. stable@vger.kernel.org
+ 2. stable@kernel.org
+
+ For adding version info, the following comment style should be used::
+
+ stable@vger.kernel.org # version info
+
+ **COMMIT_COMMENT_SYMBOL**
+ Commit log lines starting with a '#' are ignored by git as
+ comments. To solve this problem addition of a single space
+ infront of the log line is enough.
+
+ **COMMIT_MESSAGE**
+ The patch is missing a commit description. A brief
+ description of the changes made by the patch should be added.
+
+ See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#describe-your-changes
+
+ **EMAIL_SUBJECT**
+ Naming the tool that found the issue is not very useful in the
+ subject line. A good subject line summarizes the change that
+ the patch brings.
+
+ See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#describe-your-changes
+
+ **FROM_SIGN_OFF_MISMATCH**
+ The author's email does not match with that in the Signed-off-by:
+ line(s). This can be sometimes caused due to an improperly configured
+ email client.
+
+ This message is emitted due to any of the following reasons::
+
+ - The email names do not match.
+ - The email addresses do not match.
+ - The email subaddresses do not match.
+ - The email comments do not match.
+
+ **MISSING_SIGN_OFF**
+ The patch is missing a Signed-off-by line. A signed-off-by
+ line should be added according to Developer's certificate of
+ Origin.
+
+ See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin
+
+ **NO_AUTHOR_SIGN_OFF**
+ The author of the patch has not signed off the patch. It is
+ required that a simple sign off line should be present at the
+ end of explanation of the patch to denote that the author has
+ written it or otherwise has the rights to pass it on as an open
+ source patch.
+
+ See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin
+
+ **DIFF_IN_COMMIT_MSG**
+ Avoid having diff content in commit message.
+ This causes problems when one tries to apply a file containing both
+ the changelog and the diff because patch(1) tries to apply the diff
+ which it found in the changelog.
+
+ See: https://lore.kernel.org/lkml/20150611134006.9df79a893e3636019ad2759e@linux-foundation.org/
+
+ **GERRIT_CHANGE_ID**
+ To be picked up by gerrit, the footer of the commit message might
+ have a Change-Id like::
+
+ Change-Id: Ic8aaa0728a43936cd4c6e1ed590e01ba8f0fbf5b
+ Signed-off-by: A. U. Thor <author@example.com>
+
+ The Change-Id line must be removed before submitting.
+
+ **GIT_COMMIT_ID**
+ The proper way to reference a commit id is:
+ commit <12+ chars of sha1> ("<title line>")
+
+ An example may be::
+
+ Commit e21d2170f36602ae2708 ("video: remove unnecessary
+ platform_set_drvdata()") removed the unnecessary
+ platform_set_drvdata(), but left the variable "dev" unused,
+ delete it.
+
+ See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#describe-your-changes
+
+
+Comparison style
+----------------
+
+ **ASSIGN_IN_IF**
+ Do not use assignments in if condition.
+ Example::
+
+ if ((foo = bar(...)) < BAZ) {
+
+ should be written as::
+
+ foo = bar(...);
+ if (foo < BAZ) {
+
+ **BOOL_COMPARISON**
+ Comparisons of A to true and false are better written
+ as A and !A.
+
+ See: https://lore.kernel.org/lkml/1365563834.27174.12.camel@joe-AO722/
+
+ **COMPARISON_TO_NULL**
+ Comparisons to NULL in the form (foo == NULL) or (foo != NULL)
+ are better written as (!foo) and (foo).
+
+ **CONSTANT_COMPARISON**
+ Comparisons with a constant or upper case identifier on the left
+ side of the test should be avoided.
+
+
+Indentation and Line Breaks
+---------------------------
+
+ **CODE_INDENT**
+ Code indent should use tabs instead of spaces.
+ Outside of comments, documentation and Kconfig,
+ spaces are never used for indentation.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#indentation
+
+ **DEEP_INDENTATION**
+ Indentation with 6 or more tabs usually indicate overly indented
+ code.
+
+ It is suggested to refactor excessive indentation of
+ if/else/for/do/while/switch statements.
+
+ See: https://lore.kernel.org/lkml/1328311239.21255.24.camel@joe2Laptop/
+
+ **SWITCH_CASE_INDENT_LEVEL**
+ switch should be at the same indent as case.
+ Example::
+
+ switch (suffix) {
+ case 'G':
+ case 'g':
+ mem <<= 30;
+ break;
+ case 'M':
+ case 'm':
+ mem <<= 20;
+ break;
+ case 'K':
+ case 'k':
+ mem <<= 10;
+ fallthrough;
+ default:
+ break;
+ }
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#indentation
+
+ **LONG_LINE**
+ The line has exceeded the specified maximum length.
+ To use a different maximum line length, the --max-line-length=n option
+ may be added while invoking checkpatch.
+
+ Earlier, the default line length was 80 columns. Commit bdc48fa11e46
+ ("checkpatch/coding-style: deprecate 80-column warning") increased the
+ limit to 100 columns. This is not a hard limit either and it's
+ preferable to stay within 80 columns whenever possible.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings
+
+ **LONG_LINE_STRING**
+ A string starts before but extends beyond the maximum line length.
+ To use a different maximum line length, the --max-line-length=n option
+ may be added while invoking checkpatch.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings
+
+ **LONG_LINE_COMMENT**
+ A comment starts before but extends beyond the maximum line length.
+ To use a different maximum line length, the --max-line-length=n option
+ may be added while invoking checkpatch.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings
+
+ **SPLIT_STRING**
+ Quoted strings that appear as messages in userspace and can be
+ grepped, should not be split across multiple lines.
+
+ See: https://lore.kernel.org/lkml/20120203052727.GA15035@leaf/
+
+ **MULTILINE_DEREFERENCE**
+ A single dereferencing identifier spanned on multiple lines like::
+
+ struct_identifier->member[index].
+ member = <foo>;
+
+ is generally hard to follow. It can easily lead to typos and so makes
+ the code vulnerable to bugs.
+
+ If fixing the multiple line dereferencing leads to an 80 column
+ violation, then either rewrite the code in a more simple way or if the
+ starting part of the dereferencing identifier is the same and used at
+ multiple places then store it in a temporary variable, and use that
+ temporary variable only at all the places. For example, if there are
+ two dereferencing identifiers::
+
+ member1->member2->member3.foo1;
+ member1->member2->member3.foo2;
+
+ then store the member1->member2->member3 part in a temporary variable.
+ It not only helps to avoid the 80 column violation but also reduces
+ the program size by removing the unnecessary dereferences.
+
+ But if none of the above methods work then ignore the 80 column
+ violation because it is much easier to read a dereferencing identifier
+ on a single line.
+
+ **TRAILING_STATEMENTS**
+ Trailing statements (for example after any conditional) should be
+ on the next line.
+ Statements, such as::
+
+ if (x == y) break;
+
+ should be::
+
+ if (x == y)
+ break;
+
+
+Macros, Attributes and Symbols
+------------------------------
+
+ **AVOID_EXTERNS**
+ Function prototypes don't need to be declared extern in .h
+ files. It's assumed by the compiler and is unnecessary.
+
+ **AVOID_L_PREFIX**
+ Local symbol names that are prefixed with `.L` should be avoided,
+ as this has special meaning for the assembler; a symbol entry will
+ not be emitted into the symbol table. This can prevent `objtool`
+ from generating correct unwind info.
+
+ Symbols with STB_LOCAL binding may still be used, and `.L` prefixed
+ local symbol names are still generally usable within a function,
+ but `.L` prefixed local symbol names should not be used to denote
+ the beginning or end of code regions via
+ `SYM_CODE_START_LOCAL`/`SYM_CODE_END`
+
+ **BIT_MACRO**
+ Defines like: 1 << <digit> could be BIT(digit).
+ The BIT() macro is defined via include/linux/bits.h::
+
+ #define BIT(nr) (1UL << (nr))
+
+ **CONST_READ_MOSTLY**
+ When a variable is tagged with the __read_mostly annotation, it is a
+ signal to the compiler that accesses to the variable will be mostly
+ reads and rarely(but NOT never) a write.
+
+ const __read_mostly does not make any sense as const data is already
+ read-only. The __read_mostly annotation thus should be removed.
+
+ **DATE_TIME**
+ It is generally desirable that building the same source code with
+ the same set of tools is reproducible, i.e. the output is always
+ exactly the same.
+
+ The kernel does *not* use the ``__DATE__`` and ``__TIME__`` macros,
+ and enables warnings if they are used as they can lead to
+ non-deterministic builds.
+
+ See: https://www.kernel.org/doc/html/latest/kbuild/reproducible-builds.html#timestamps
+
+ **DEFINE_ARCH_HAS**
+ The ARCH_HAS_xyz and ARCH_HAVE_xyz patterns are wrong.
+
+ For big conceptual features use Kconfig symbols instead. And for
+ smaller things where we have compatibility fallback functions but
+ want architectures able to override them with optimized ones, we
+ should either use weak functions (appropriate for some cases), or
+ the symbol that protects them should be the same symbol we use.
+
+ See: https://lore.kernel.org/lkml/CA+55aFycQ9XJvEOsiM3txHL5bjUc8CeKWJNR_H+MiicaddB42Q@mail.gmail.com/
+
+ **DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON**
+ do {} while(0) macros should not have a trailing semicolon.
+
+ **INIT_ATTRIBUTE**
+ Const init definitions should use __initconst instead of
+ __initdata.
+
+ Similarly init definitions without const require a separate
+ use of const.
+
+ **INLINE_LOCATION**
+ The inline keyword should sit between storage class and type.
+
+ For example, the following segment::
+
+ inline static int example_function(void)
+ {
+ ...
+ }
+
+ should be::
+
+ static inline int example_function(void)
+ {
+ ...
+ }
+
+ **MISPLACED_INIT**
+ It is possible to use section markers on variables in a way
+ which gcc doesn't understand (or at least not the way the
+ developer intended)::
+
+ static struct __initdata samsung_pll_clock exynos4_plls[nr_plls] = {
+
+ does not put exynos4_plls in the .initdata section. The __initdata
+ marker can be virtually anywhere on the line, except right after
+ "struct". The preferred location is before the "=" sign if there is
+ one, or before the trailing ";" otherwise.
+
+ See: https://lore.kernel.org/lkml/1377655732.3619.19.camel@joe-AO722/
+
+ **MULTISTATEMENT_MACRO_USE_DO_WHILE**
+ Macros with multiple statements should be enclosed in a
+ do - while block. Same should also be the case for macros
+ starting with `if` to avoid logic defects::
+
+ #define macrofun(a, b, c) \
+ do { \
+ if (a == 5) \
+ do_this(b, c); \
+ } while (0)
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#macros-enums-and-rtl
+
+ **PREFER_FALLTHROUGH**
+ Use the `fallthrough;` pseudo keyword instead of
+ `/* fallthrough */` like comments.
+
+ **TRAILING_SEMICOLON**
+ Macro definition should not end with a semicolon. The macro
+ invocation style should be consistent with function calls.
+ This can prevent any unexpected code paths::
+
+ #define MAC do_something;
+
+ If this macro is used within a if else statement, like::
+
+ if (some_condition)
+ MAC;
+
+ else
+ do_something;
+
+ Then there would be a compilation error, because when the macro is
+ expanded there are two trailing semicolons, so the else branch gets
+ orphaned.
+
+ See: https://lore.kernel.org/lkml/1399671106.2912.21.camel@joe-AO725/
+
+ **SINGLE_STATEMENT_DO_WHILE_MACRO**
+ For the multi-statement macros, it is necessary to use the do-while
+ loop to avoid unpredictable code paths. The do-while loop helps to
+ group the multiple statements into a single one so that a
+ function-like macro can be used as a function only.
+
+ But for the single statement macros, it is unnecessary to use the
+ do-while loop. Although the code is syntactically correct but using
+ the do-while loop is redundant. So remove the do-while loop for single
+ statement macros.
+
+ **WEAK_DECLARATION**
+ Using weak declarations like __attribute__((weak)) or __weak
+ can have unintended link defects. Avoid using them.
+
+
+Functions and Variables
+-----------------------
+
+ **CAMELCASE**
+ Avoid CamelCase Identifiers.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#naming
+
+ **CONST_CONST**
+ Using `const <type> const *` is generally meant to be
+ written `const <type> * const`.
+
+ **CONST_STRUCT**
+ Using const is generally a good idea. Checkpatch reads
+ a list of frequently used structs that are always or
+ almost always constant.
+
+ The existing structs list can be viewed from
+ `scripts/const_structs.checkpatch`.
+
+ See: https://lore.kernel.org/lkml/alpine.DEB.2.10.1608281509480.3321@hadrien/
+
+ **EMBEDDED_FUNCTION_NAME**
+ Embedded function names are less appropriate to use as
+ refactoring can cause function renaming. Prefer the use of
+ "%s", __func__ to embedded function names.
+
+ Note that this does not work with -f (--file) checkpatch option
+ as it depends on patch context providing the function name.
+
+ **FUNCTION_ARGUMENTS**
+ This warning is emitted due to any of the following reasons:
+
+ 1. Arguments for the function declaration do not follow
+ the identifier name. Example::
+
+ void foo
+ (int bar, int baz)
+
+ This should be corrected to::
+
+ void foo(int bar, int baz)
+
+ 2. Some arguments for the function definition do not
+ have an identifier name. Example::
+
+ void foo(int)
+
+ All arguments should have identifier names.
+
+ **FUNCTION_WITHOUT_ARGS**
+ Function declarations without arguments like::
+
+ int foo()
+
+ should be::
+
+ int foo(void)
+
+ **GLOBAL_INITIALISERS**
+ Global variables should not be initialized explicitly to
+ 0 (or NULL, false, etc.). Your compiler (or rather your
+ loader, which is responsible for zeroing out the relevant
+ sections) automatically does it for you.
+
+ **INITIALISED_STATIC**
+ Static variables should not be initialized explicitly to zero.
+ Your compiler (or rather your loader) automatically does
+ it for you.
+
+ **MULTIPLE_ASSIGNMENTS**
+ Multiple assignments on a single line makes the code unnecessarily
+ complicated. So on a single line assign value to a single variable
+ only, this makes the code more readable and helps avoid typos.
+
+ **RETURN_PARENTHESES**
+ return is not a function and as such doesn't need parentheses::
+
+ return (bar);
+
+ can simply be::
+
+ return bar;
+
+
+Permissions
+-----------
+
+ **DEVICE_ATTR_PERMS**
+ The permissions used in DEVICE_ATTR are unusual.
+ Typically only three permissions are used - 0644 (RW), 0444 (RO)
+ and 0200 (WO).
+
+ See: https://www.kernel.org/doc/html/latest/filesystems/sysfs.html#attributes
+
+ **EXECUTE_PERMISSIONS**
+ There is no reason for source files to be executable. The executable
+ bit can be removed safely.
+
+ **EXPORTED_WORLD_WRITABLE**
+ Exporting world writable sysfs/debugfs files is usually a bad thing.
+ When done arbitrarily they can introduce serious security bugs.
+ In the past, some of the debugfs vulnerabilities would seemingly allow
+ any local user to write arbitrary values into device registers - a
+ situation from which little good can be expected to emerge.
+
+ See: https://lore.kernel.org/linux-arm-kernel/cover.1296818921.git.segoon@openwall.com/
+
+ **NON_OCTAL_PERMISSIONS**
+ Permission bits should use 4 digit octal permissions (like 0700 or 0444).
+ Avoid using any other base like decimal.
+
+ **SYMBOLIC_PERMS**
+ Permission bits in the octal form are more readable and easier to
+ understand than their symbolic counterparts because many command-line
+ tools use this notation. Experienced kernel developers have been using
+ these traditional Unix permission bits for decades and so they find it
+ easier to understand the octal notation than the symbolic macros.
+ For example, it is harder to read S_IWUSR|S_IRUGO than 0644, which
+ obscures the developer's intent rather than clarifying it.
+
+ See: https://lore.kernel.org/lkml/CA+55aFw5v23T-zvDZp-MmD_EYxF8WbafwwB59934FV7g21uMGQ@mail.gmail.com/
+
+
+Spacing and Brackets
+--------------------
+
+ **ASSIGNMENT_CONTINUATIONS**
+ Assignment operators should not be written at the start of a
+ line but should follow the operand at the previous line.
+
+ **BRACES**
+ The placement of braces is stylistically incorrect.
+ The preferred way is to put the opening brace last on the line,
+ and put the closing brace first::
+
+ if (x is true) {
+ we do y
+ }
+
+ This applies for all non-functional blocks.
+ However, there is one special case, namely functions: they have the
+ opening brace at the beginning of the next line, thus::
+
+ int function(int x)
+ {
+ body of function
+ }
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces
+
+ **BRACKET_SPACE**
+ Whitespace before opening bracket '[' is prohibited.
+ There are some exceptions:
+
+ 1. With a type on the left::
+
+ int [] a;
+
+ 2. At the beginning of a line for slice initialisers::
+
+ [0...10] = 5,
+
+ 3. Inside a curly brace::
+
+ = { [0...10] = 5 }
+
+ **CONCATENATED_STRING**
+ Concatenated elements should have a space in between.
+ Example::
+
+ printk(KERN_INFO"bar");
+
+ should be::
+
+ printk(KERN_INFO "bar");
+
+ **ELSE_AFTER_BRACE**
+ `else {` should follow the closing block `}` on the same line.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces
+
+ **LINE_SPACING**
+ Vertical space is wasted given the limited number of lines an
+ editor window can display when multiple blank lines are used.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#spaces
+
+ **OPEN_BRACE**
+ The opening brace should be following the function definitions on the
+ next line. For any non-functional block it should be on the same line
+ as the last construct.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces
+
+ **POINTER_LOCATION**
+ When using pointer data or a function that returns a pointer type,
+ the preferred use of * is adjacent to the data name or function name
+ and not adjacent to the type name.
+ Examples::
+
+ char *linux_banner;
+ unsigned long long memparse(char *ptr, char **retptr);
+ char *match_strdup(substring_t *s);
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#spaces
+
+ **SPACING**
+ Whitespace style used in the kernel sources is described in kernel docs.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#spaces
+
+ **TRAILING_WHITESPACE**
+ Trailing whitespace should always be removed.
+ Some editors highlight the trailing whitespace and cause visual
+ distractions when editing files.
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#spaces
+
+ **UNNECESSARY_PARENTHESES**
+ Parentheses are not required in the following cases:
+
+ 1. Function pointer uses::
+
+ (foo->bar)();
+
+ could be::
+
+ foo->bar();
+
+ 2. Comparisons in if::
+
+ if ((foo->bar) && (foo->baz))
+ if ((foo == bar))
+
+ could be::
+
+ if (foo->bar && foo->baz)
+ if (foo == bar)
+
+ 3. addressof/dereference single Lvalues::
+
+ &(foo->bar)
+ *(foo->bar)
+
+ could be::
+
+ &foo->bar
+ *foo->bar
+
+ **WHILE_AFTER_BRACE**
+ while should follow the closing bracket on the same line::
+
+ do {
+ ...
+ } while(something);
+
+ See: https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces
+
+
+Others
+------
+
+ **CONFIG_DESCRIPTION**
+ Kconfig symbols should have a help text which fully describes
+ it.
+
+ **CORRUPTED_PATCH**
+ The patch seems to be corrupted or lines are wrapped.
+ Please regenerate the patch file before sending it to the maintainer.
+
+ **CVS_KEYWORD**
+ Since linux moved to git, the CVS markers are no longer used.
+ So, CVS style keywords ($Id$, $Revision$, $Log$) should not be
+ added.
+
+ **DEFAULT_NO_BREAK**
+ switch default case is sometimes written as "default:;". This can
+ cause new cases added below default to be defective.
+
+ A "break;" should be added after empty default statement to avoid
+ unwanted fallthrough.
+
+ **DOS_LINE_ENDINGS**
+ For DOS-formatted patches, there are extra ^M symbols at the end of
+ the line. These should be removed.
+
+ **DT_SCHEMA_BINDING_PATCH**
+ DT bindings moved to a json-schema based format instead of
+ freeform text.
+
+ See: https://www.kernel.org/doc/html/latest/devicetree/bindings/writing-schema.html
+
+ **DT_SPLIT_BINDING_PATCH**
+ Devicetree bindings should be their own patch. This is because
+ bindings are logically independent from a driver implementation,
+ they have a different maintainer (even though they often
+ are applied via the same tree), and it makes for a cleaner history in the
+ DT only tree created with git-filter-branch.
+
+ See: https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters
+
+ **EMBEDDED_FILENAME**
+ Embedding the complete filename path inside the file isn't particularly
+ useful as often the path is moved around and becomes incorrect.
+
+ **FILE_PATH_CHANGES**
+ Whenever files are added, moved, or deleted, the MAINTAINERS file
+ patterns can be out of sync or outdated.
+
+ So MAINTAINERS might need updating in these cases.
+
+ **MEMSET**
+ The memset use appears to be incorrect. This may be caused due to
+ badly ordered parameters. Please recheck the usage.
+
+ **NOT_UNIFIED_DIFF**
+ The patch file does not appear to be in unified-diff format. Please
+ regenerate the patch file before sending it to the maintainer.
+
+ **PRINTF_0XDECIMAL**
+ Prefixing 0x with decimal output is defective and should be corrected.
+
+ **SPDX_LICENSE_TAG**
+ The source file is missing or has an improper SPDX identifier tag.
+ The Linux kernel requires the precise SPDX identifier in all source files,
+ and it is thoroughly documented in the kernel docs.
+
+ See: https://www.kernel.org/doc/html/latest/process/license-rules.html
+
+ **TYPO_SPELLING**
+ Some words may have been misspelled. Consider reviewing them.
diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst
index 61b9cf6acb..59073b39ab 100644
--- a/doc/developer/cli.rst
+++ b/doc/developer/cli.rst
@@ -639,13 +639,14 @@ in order into ``*argv[]``. Before this happens the ``->arg`` field is set to
point at the snippet of user input that matched it.
For most nontrivial commands the handler function will need to determine which
-of the possible matching inputs was entered. Previously this was done by looking
-at the first few characters of input. This is now considered an anti-pattern and
-should be avoided. Instead, the ``->type`` or ``->text`` fields for this logic.
-The ``->type`` field can be used when the possible inputs differ in type. When
-the possible types are the same, use the ``->text`` field. This field has the
-full text of the corresponding token in the definition string and using it makes
-for much more readable code. An example is helpful.
+of the possible matching inputs was entered. Previously this was done by
+looking at the first few characters of input. This is now considered an
+anti-pattern and should be avoided. Instead, use the ``->type`` or ``->text``
+fields for this logic. The ``->type`` field can be used when the possible
+inputs differ in type. When the possible types are the same, use the ``->text``
+field. This field has the full text of the corresponding token in the
+definition string and using it makes for much more readable code. An example is
+helpful.
Command definition:
@@ -654,9 +655,10 @@ Command definition:
command <(1-10)|foo|BAR>
In this example, the user may enter any one of:
-- an integer between 1 and 10
-- "foo"
-- anything at all
+
+* an integer between 1 and 10
+* "foo"
+* anything at all
If the user enters "command f", then:
@@ -793,12 +795,12 @@ Adding a CLI Node
To add a new CLI node, you should:
-- define a new numerical node constant
-- define a node structure in the relevant daemon
-- call ``install_node()`` in the relevant daemon
-- define and install the new node in vtysh
-- define corresponding node entry commands in daemon and vtysh
-- add a new entry to the ``ctx_keywords`` dictionary in ``tools/frr-reload.py``
+#. define a new numerical node constant
+#. define a node structure in the relevant daemon
+#. call ``install_node()`` in the relevant daemon
+#. define and install the new node in vtysh
+#. define corresponding node entry commands in daemon and vtysh
+#. add a new entry to the ``ctx_keywords`` dictionary in ``tools/frr-reload.py``
Defining the numerical node constant
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/doc/developer/cross-compiling.rst b/doc/developer/cross-compiling.rst
index 3bf78f7633..af99262c4f 100644
--- a/doc/developer/cross-compiling.rst
+++ b/doc/developer/cross-compiling.rst
@@ -239,9 +239,9 @@ the last thing to actually build is FRR itself:
--host=${HOST_ARCH} \
--with-sysroot=/usr/${HOST_ARCH} \
--with-clippy=./build-clippy/lib/clippy \
- --sysconfdir=/etc/frr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
--sbindir="\${prefix}/lib/frr" \
- --localstatedir=/var/run/frr \
--prefix=/usr \
--enable-user=frr \
--enable-group=frr \
diff --git a/doc/developer/cspf.rst b/doc/developer/cspf.rst
index 426553ff06..7a5a55ee31 100644
--- a/doc/developer/cspf.rst
+++ b/doc/developer/cspf.rst
@@ -24,59 +24,59 @@ to the cost of the cuurent path from the source up to the current node.
The algorithm is as followed:
-```
- cost = MAX_COST;
- Priority_Queue.empty();
- Visited_Node.empty();
- Processed_Path.empty();
- src = new_path(source_address);
- src.cost = 0;
- dst = new_destinatio(destination_address);
- dst.cost = MAX_COST;
- Processed_Path.add(src);
- Processed_Path.add(dst);
- while (Priority_Queue.count != 0) {
- current_path = Priority_Queue.pop();
- current_node = next_path.destination;
- Visited_Node.add(current_node);
- for (current_node.edges: edge) {
- if (prune_edge(current_path, edge)
- continue;
- if (relax(current_path) && cost > current_path.cost) {
- optim_path = current_path;
- cost = current_path.cost;
- }
- }
- }
-
- prune_edge(path, edge) {
- // check that path + edge meet constraints e.g.
- if (current_path.cost + edge.cost > constrained_cost)
- return false;
- else
- return true;
- }
-
- relax_edge(current_path, edge) {
- next_node = edge.destination;
- if (Visited_Node.get(next_node))
- return false;
- next_path = Processed_Path.get(edge.destination);
- if (!next_path) {
- next_path = new path(edge.destination);
- Processed_Path.add(next_path);
- }
- total_cost = current_path.cost + edge.cost;
- if (total_cost < next_path.cost) {
- next_path = current_path;
- next_path.add_edge(edge);
- next_path.cost = total_cost;
- Priority_Queue.add(next_path);
- }
- return (next_path.destination == destination);
- }
-
-```
+.. code-block:: c
+
+ cost = MAX_COST;
+ Priority_Queue.empty();
+ Visited_Node.empty();
+ Processed_Path.empty();
+ src = new_path(source_address);
+ src.cost = 0;
+ dst = new_destinatio(destination_address);
+ dst.cost = MAX_COST;
+ Processed_Path.add(src);
+ Processed_Path.add(dst);
+ while (Priority_Queue.count != 0) {
+ current_path = Priority_Queue.pop();
+ current_node = next_path.destination;
+ Visited_Node.add(current_node);
+ for (current_node.edges: edge) {
+ if (prune_edge(current_path, edge)
+ continue;
+ if (relax(current_path) && cost > current_path.cost) {
+ optim_path = current_path;
+ cost = current_path.cost;
+ }
+ }
+ }
+
+ prune_edge(path, edge) {
+ // check that path + edge meet constraints e.g.
+ if (current_path.cost + edge.cost > constrained_cost)
+ return false;
+ else
+ return true;
+ }
+
+ relax_edge(current_path, edge) {
+ next_node = edge.destination;
+ if (Visited_Node.get(next_node))
+ return false;
+ next_path = Processed_Path.get(edge.destination);
+ if (!next_path) {
+ next_path = new path(edge.destination);
+ Processed_Path.add(next_path);
+ }
+ total_cost = current_path.cost + edge.cost;
+ if (total_cost < next_path.cost) {
+ next_path = current_path;
+ next_path.add_edge(edge);
+ next_path.cost = total_cost;
+ Priority_Queue.add(next_path);
+ }
+ return (next_path.destination == destination);
+ }
+
Definition
----------
@@ -162,34 +162,35 @@ various metrics. Link State provides such Traffic Engineering Database.
To perform a Path Computation with given constraints, proceed as follow:
.. code-block:: c
- struct cspf *algo;
- struct ls_ted *ted;
- struct in_addr src;
- struct in_addr dst;
- struct constraints csts;
- struct c_path *path;
-
- // Create a new CSPF structure
- algo = cspf_new();
-
- // Initialize constraints
- csts.cost = 100;
- csts.ctype = CSPF_TE_METRIC;
- csts.family = AF_INET;
- csts.type = SR_TE;
- csts.bw = 1000000;
- csts.cos = 3;
-
- // Then, initialise th CSPF with source, destination and constraints
- cspf_init_v4(algo, ted, src, dst, &csts);
-
- // Finally, got the Computed Path;
- path = compute_p2p_path(ted, algo);
-
- if (path.status == SUCCESS)
- zlog_info("Got a valid constraints path");
- else
- zlog_info("Unable to compute constraints path. Got %d status", path->status);
+
+ struct cspf *algo;
+ struct ls_ted *ted;
+ struct in_addr src;
+ struct in_addr dst;
+ struct constraints csts;
+ struct c_path *path;
+
+ // Create a new CSPF structure
+ algo = cspf_new();
+
+ // Initialize constraints
+ csts.cost = 100;
+ csts.ctype = CSPF_TE_METRIC;
+ csts.family = AF_INET;
+ csts.type = SR_TE;
+ csts.bw = 1000000;
+ csts.cos = 3;
+
+ // Then, initialise th CSPF with source, destination and constraints
+ cspf_init_v4(algo, ted, src, dst, &csts);
+
+ // Finally, got the Computed Path;
+ path = compute_p2p_path(ted, algo);
+
+ if (path.status == SUCCESS)
+ zlog_info("Got a valid constraints path");
+ else
+ zlog_info("Unable to compute constraints path. Got %d status", path->status);
If you would compute another path, you must call `cspf_init()` prior to
diff --git a/doc/developer/frr-release-procedure.rst b/doc/developer/frr-release-procedure.rst
index 6088b52da4..9dbc9b48d7 100644
--- a/doc/developer/frr-release-procedure.rst
+++ b/doc/developer/frr-release-procedure.rst
@@ -13,6 +13,13 @@ Stage 1 - Preparation
Note: use ``tools/release_notes.py`` to help draft release notes changelog
+ .. code-block:: console
+
+ ./tools/release_notes.py -b dev/9.1 -t frr-9.0.1
+
+ dev/9.1 is the branch to be renamed to stable/9.1, and frr-9.0.1 in this
+ example is the latest tag from which to generate the logs.
+
#. Checkout the existing ``dev/<version>`` branch.
.. code-block:: console
@@ -152,11 +159,11 @@ Stage 2 - Staging
3. Suppose we are releasing 8.5.0, then ``X.Y.Z`` is ``8.5.0``. Run this:
.. code-block:: console
-
+
cd /home/builduser/frr
TAG=X.Y.Z
git fetch --all
- git checkout frr-<version>
+ git checkout frr-$TAG
docker buildx build --platform linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/arm/v7,linux/arm/v6 -f docker/alpine/Dockerfile -t quay.io/frrouting/frr:$TAG --push .
git tag docker/$TAG
git push origin docker/$TAG
@@ -165,7 +172,7 @@ Stage 2 - Staging
create a git tag corresponding to the commit that the image was built
from and upload that to Github. It's important that the git tag point to
the exact codebase that was used to build the docker image, so if any
- changes need to be made on top of the ``frr-<version>`` release tag, make
+ changes need to be made on top of the ``frr-$TAG`` release tag, make
sure these changes are committed and pointed at by the ``docker/X.Y.Z``
tag.
diff --git a/doc/developer/include-compile.rst b/doc/developer/include-compile.rst
index 513cac6179..49fd6c854c 100644
--- a/doc/developer/include-compile.rst
+++ b/doc/developer/include-compile.rst
@@ -14,10 +14,9 @@ obtained by running ``./configure -h``. The options shown below are examples.
--sbindir=\${prefix}/lib/frr \
--libdir=\${prefix}/lib/frr \
--libexecdir=\${prefix}/lib/frr \
- --localstatedir=/var/run/frr \
- --sysconfdir=/etc/frr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
--with-moduledir=\${prefix}/lib/frr/modules \
- --with-libyang-pluginsdir=\${prefix}/lib/frr/libyang_plugins \
--enable-configfile-mask=0640 \
--enable-logfile-mask=0640 \
--enable-snmp=agentx \
diff --git a/doc/developer/index.rst b/doc/developer/index.rst
index 46fd8f612e..bd794b11a8 100644
--- a/doc/developer/index.rst
+++ b/doc/developer/index.rst
@@ -5,6 +5,7 @@ FRRouting Developer's Guide
:maxdepth: 2
workflow
+ checkpatch
building
packaging
process-architecture
@@ -12,6 +13,7 @@ FRRouting Developer's Guide
fuzzing
tracing
testing
+ mgmtd-dev
bgpd
fpm
grpc
@@ -21,3 +23,4 @@ FRRouting Developer's Guide
path
pceplib
link-state
+ northbound/northbound
diff --git a/doc/developer/link-state.rst b/doc/developer/link-state.rst
index 2072595e36..aaa253de94 100644
--- a/doc/developer/link-state.rst
+++ b/doc/developer/link-state.rst
@@ -9,7 +9,7 @@ build and manage a Traffic Engineering Database for the various FRR daemons.
This API has been designed for several use cases:
- BGP Link State (BGP-LS): where BGP protocol need to collect the link state
- information from the routing daemons (IS-IS and/or OSPF) to implement RFC7572
+ information from the routing daemons (IS-IS and/or OSPF) to implement RFC7752
- Path Computation Element (PCE): where path computation algorithms are based
on Traffic Engineering Database
- ReSerVation Protocol (RSVP): where signaling need to know the Traffic
diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst
index 52653d3768..82cc8b205e 100644
--- a/doc/developer/logging.rst
+++ b/doc/developer/logging.rst
@@ -77,7 +77,14 @@ are available:
.. note::
- ``printfrr()`` does not support the ``%n`` format.
+ ``printfrr()`` does not support the ``%n`` format. It does support ISO C23
+ ``%b``, ``%w99d`` and ``%wf99d`` additions, but the latter two are not
+ supported by the ``frr-format`` plugin yet, and all 3 aren't supported by
+ the older compilers still in use on some supported platforms.
+
+ ``%b`` can be used with ``FMT_NSTD``, but ``%w99d`` and ``%wf99d`` require
+ work in the ``frr-format`` plugin before they are really usable.
+
AS-Safety
^^^^^^^^^
@@ -376,7 +383,8 @@ bgpd
.. frrfmt:: %pBD (struct bgp_dest *)
- Print prefix for a BGP destination.
+ Print prefix for a BGP destination. When using ``--enable-dev-build`` include
+ the pointer value for the bgp_dest.
:frrfmtout:`fe80::1234/64`
@@ -557,8 +565,9 @@ Integer formats
cause compiler warnings when used without the plugin. Use with
:c:macro:`FMT_NSTD` if necessary.
- It is possible ISO C23 may introduce another format for these, possibly
- ``%w64d`` discussed in `JTC 1/SC 22/WG 14/N2680 <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2680.pdf>`_.
+ As anticipated, ISO C23 has introduced new modifiers for this, specifically
+ ``%w64d`` (= ``%Ld``) and ``%w64u`` (= ``%Lu``). Unfortunately, these new
+ modifiers are not supported by ``frr-format`` yet.
.. frrfmt:: %Lu (uint64_t)
diff --git a/doc/developer/mgmtd-dev.rst b/doc/developer/mgmtd-dev.rst
new file mode 100644
index 0000000000..2404ffe2a7
--- /dev/null
+++ b/doc/developer/mgmtd-dev.rst
@@ -0,0 +1,383 @@
+..
+.. SPDX-License-Identifier: GPL-2.0-or-later
+..
+.. June 19 2023, Christian Hopps <chopps@labn.net>
+..
+.. Copyright (c) 2023, LabN Consulting, L.L.C.
+..
+
+.. _mgmtd_dev:
+
+MGMTD Development
+=================
+
+Overview
+--------
+
+``mgmtd`` (Management Daemon) is a new centralized management daemon for FRR.
+
+Previously, ``vtysh`` was the only centralized management service provided.
+Internally ``vtysh`` connects to each daemon and sends CLI commands (both
+configuration and operational state queries) over a socket connection. This
+service only supports CLI which is no longer sufficient.
+
+An important next step was made with the addition of YANG support. A YANG
+infrastructure was added through a new development called *northbound*. This
+*northbound* interface added the capability of daemons to be configured and
+queried using YANG models. However, this interface was per daemon and not
+centralized, which is not sufficient.
+
+``mgmtd`` harnesses this new *northbound* interface to provide a centralized
+interface for all daemons. It utilizes the daemons YANG models to interact with
+each daemon. ``mgmtd`` currently provides the CLI interface for each daemon that
+has been converted to it, but in the future RESTCONF and NETCONF servers can
+easily be added as *front-ends* to mgmtd to support those protocols as well.
+
+Conversion Status
+^^^^^^^^^^^^^^^^^
+
+Fully Converted To MGMTD
+""""""""""""""""""""""""
+
+- lib/distribute
+- lib/filter
+- lib/if_rmap
+- lib/routemap
+- lib/affinitymap
+- lib/if
+- lib/vrf
+- ripd
+- ripngd
+- staticd
+- zebra (* - partial)
+
+Converted To Northbound
+"""""""""""""""""""""""
+- bfdd
+- pathd
+- pbrd
+- pimd
+
+Converted To Northbound With Issues
+"""""""""""""""""""""""""""""""""""
+- eigrp
+- isisd
+
+Unconverted
+"""""""""""
+- babel
+- bgpd
+- ldpd
+- lib/event
+- lib/keychain
+- lib/log_vty
+- lib/nexthop_group
+- lib/zlog_5424_cli
+- nhrpd
+- ospfd
+- ospf6d
+- pceplib
+- qdb
+- sharpd
+- vrrpd
+
+Converting A Daemon to MGMTD
+----------------------------
+
+A daemon must first be transitioned to the new :ref:`northbound` interface if that
+has not already been done (see :ref:`nb-retrofit` for how to do this). Once this
+is done a few simple steps are all that is required move the daemon over to
+``mgmtd`` control.
+
+Overview of Changes
+^^^^^^^^^^^^^^^^^^^
+
+Adding support for a *northbound* converted daemon involves very little work. It
+requires enabling *frontend* (CLI and YANG) and *backend* (YANG) support.
+``mgmtd`` was designed to keep this as simple as possible.
+
+Front-End Interface:
+
+#. Add YANG module file to ``mgmtd/subdir.am`` (e.g., ``yang/frr-staticd.yang.c``).
+
+#. Add CLI handler file[s] to ``mgmtd/subdir.am``. The `subdir.am` variable to
+ use is indicated in the next 2 steps.
+
+ #. [if needed] Exclude (:code:`#ifndef`) non-configuration CLI handlers from
+ CLI source file (e.g., inside :file:`staticd/static_vty.c`) and add the
+ file to :code:`nodist_mgmtd_libmgmt_be_nb_la_SOURCES` in
+ :file:`mgmtd/subdir.am`.
+
+ #. [otherwise] Remove CLI handler file from _SOURCES variable in the daemon
+ :file:`subdir.am` file (e.g in :file:`staticd/subdir.am`) and add to
+ :code:`mgmtd_libmgmtd_a_SOURCES` in :file:`mgmtd/subdir.am`.
+
+#. In order to have mgmtd try and load existing per-daemon config files, add
+ the daemon to the :code:`mgmt_daemons` array in :file:`lib/vty.c`. With the
+ official release of the mgmtd code FRR is no longer supporting per daemon log
+ files but it will take a while before all of the topotest is converted.
+
+#. In the daemon's :code:`struct frr_daemon_info` (i.e., inside it's
+ :code:`FRR_DAEMON_INFO()`) set the `.flags` bit `FRR_NO_SPLIT_CONFIG`. This
+ will keep the daemon from trying to read it's per-daemon config file as mgmtd
+ will now be doing this.
+
+#. Add the daemon's YANG module description[s] into the array
+ :code:`mgmt_yang_modules` defined in :file:`mgmtd/mgmt_main.c` (see
+ :ref:`mgmtd-config-write`). Make sure that all YANG modules that the daemon
+ uses are present in the mgmtd list. To find this list look in the daemon's
+ equivalent yang module array variable.
+
+#. Initialize the CLI handlers inside :code:`mgmt_vty_init` in :file:`mgmtd/mgmt_vty.c`.
+
+#. Direct ``vtysh`` to send CLI commands to ``mgmtd`` by modifying
+ ``vtysh/vtysh.h``. At the top of this file each daemon has a bit
+ ``#define``'d (e.g., ``#define VTYSH_STATICD 0x08000``) below this there are
+ groupings, replace all the uses of the daemons bit with ``VTYSH_MGMTD``
+ instead so that the CLI commands get properly routed to ``mgmtd`` rather than
+ the daemon now.
+
+ #. Remove initialization (and installation) of library CLI routines. These will
+ correspond with the VTYSH removals from the last step i.e.,:
+
+ - change access_list_init() to access_list_init_new(false) and remove from
+ VTYSH_ACL_CONFIG (leave in VTYSH_ACL_SHOW).
+ - remove if_cmd_init_default() => remove from VTYSH_INTERFACE_SUBSET
+ - remove if_cmd_init() => remove from VTYSH_INTERFACE_SUBSET
+ - change route_map_init() to route_map_init_new(false) and remove from
+ VTYSH_ROUTE_MAP_CONFIG (leave in VTYSH_ROUTE_MAP_SHOW).
+ - remove vrf_cmd_init(NULL) => remove from VTYSH_INTERFACE_SUBSET
+ ...
+
+Back-End Interface:
+
+#. In the daemon's main file initialize the BE client library. You add a global
+ `struct mgmt_be_client *mgmt_be_client` near the daemons `event_loop *master`
+ variable. Then where the daemon used to initialize it's CLI/VTY code replace
+ that with the client initialization by calling `mgmt_be_client_create`.
+ Likewise in the daemon's sigint cleanup code, operational walks should be
+ canceled with a call to `nb_oper_cancel_all_walks`, and then the BE client
+ should be destroyed with a call to `mgmt_be_client_destroy` and to be safe
+ NULL out the global `mgmt_be_client` variable.
+
+#. In ``mgmtd/mgmt_be_adapter.c`` add xpath prefix mappings to a one or both
+ mapping arrays (``be_client_config_xpaths`` and ``be_client_oper_xpaths``) to
+ direct ``mgmtd`` to send config and oper-state requests to your daemon. NOTE:
+ make sure to include library supported xpaths prefixes as well (e.g.,
+ "/frr-interface:lib"). A good way to figure these paths out are to look in
+ each of the YANG modules that the daemon uses and include each of their paths
+ in the array.
+
+Add YANG and CLI into MGMTD
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+As an example here is the addition made to ``mgmtd/subdir.am`` for adding
+``staticd`` support.
+
+.. code-block:: make
+
+ if STATICD
+ nodist_mgmtd_mgmtd_SOURCES += \
+ yang/frr-staticd.yang.c \
+ yang/frr-bfdd.yang.c \
+ # end
+ nodist_mgmtd_libmgmt_be_nb_la_SOURCES += staticd/static_vty.c
+ endif
+
+An here is the addition to the modules array in ``mgmtd/mgmt_main.c``:
+
+.. code-block:: c
+
+ #ifdef HAVE_STATICD
+ extern const struct frr_yang_module_info frr_staticd_info;
+ #endif
+
+ static const struct frr_yang_module_info *const mgmt_yang_modules[] = {
+ &frr_filter_info,
+ ...
+ #ifdef HAVE_STATICD
+ &frr_staticd_info,
+ #endif
+ }
+
+
+CLI Config and Show Handlers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The daemon's CLI handlers for configuration (which having been converted to the
+:ref:`northbound` now simply generate YANG changes) will be linked directly into
+``mgmtd``.
+
+If the operational and debug CLI commands are kept in files separate from the
+daemon's configuration CLI commands then no extra work is required. Otherwise some
+CPP #ifndef's will be required.
+
+``mgmtd`` supports both config and operational state. However, many
+daemons have not had their operational state CLI commands converted over to the
+new YANG based methods. If that is the case and if both types of CLI handlers
+are present in a single file (e.g. a ``xxx_vty.c`` or ``xxx_cli.c`` file) then
+:code:`#ifndef` will need to be used to exclude the non-config CLI handlers from
+``mgmtd``. The same goes for unconverted *debug* CLI handlers. For example:
+
+.. code-block:: c
+
+ DEFPY(daemon_one_config, daemon_one_config_cmd,
+ "daemon one [optional-arg]"
+ ...
+ {
+ ...
+ }
+
+ #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY
+ DEFPY(daemon_show_oper, daemon_show_oper_cmd,
+ "show daemon oper [all]"
+ ...
+ {
+ ...
+ }
+ #endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */
+
+ void daemon_vty_init(void)
+ {
+ install_element(CONFIG_NODE, &daemon_one_config_cmd);
+ ...
+
+ #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY
+ install_element(ENABLE_NODE, &daemon_show_oper_cmd);
+ #endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */
+
+ }
+
+.. _mgmtd-config-write:
+
+CLI Config Write Handlers (:code:`cli_show`)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To support writing out the CLI configuration file the northbound API defines a
+2 callbacks (:code:`cli_show` and :code:`cli_show_end`). Pointers to these
+callbacks used to live side-by-side in a daemons :code:`struct frr_yang_module_info`,
+with the daemons back-end configuration and operational state callbacks
+(normally in a file named `<daemon>_nb.c`).
+
+However, these 2 functionalities need to be split up now. The *frontend* config
+writing callbacks (:code:`cli_show`) should now be linked into ``mgmtd`` while
+the *backend* config and oper-state callbacks (e.g., :code:`create`,
+:code:`modify`, etc) should continue to be linked into the daemon.
+
+So you will need to define 2 :code:`struct frr_yang_module_info` arrays.
+
+#. The existing array remains in the same place in the daemon, but with all the
+ :code:`cli_show` handlers removed.
+
+#. The removed :code:`cli_show` handlers should be added to a new
+ :code:`struct frr_yang_module_info` array. This second array should be
+ included in the same file that includes that actual function pointed to by
+ the the :code:`cli_show` callbacks (i.e., the file is compiled into
+ ``mgmtd``).
+
+ This new :code:`struct frr_yang_module_info` array is the one to be included
+ in mgmtd in `mgmt_yang_modules` inside ``mgmtd/mgmt_main.c``.
+
+Back-End Client Connection
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In order for your daemon to communicate with mgmtd you need to initialize the
+backend client library. You normally do this where you used to initialize your
+CLI/VTY code.
+
+.. code-block:: c
+
+ ...
+ struct event_loop *master;
+
+ static struct mgmt_be_client *mgmt_be_client;
+ ...
+
+ int main(int argc, char **argv)
+ {
+ ...
+ rip_init();
+ rip_if_init();
+ mgmt_be_client = mgmt_be_client_create("ripd", NULL, 0, master);
+
+Likewise the client should be cleaned up in the daemon cleanup routine.
+
+.. code-block:: c
+
+ /* SIGINT handler. */
+ static void sigint(void)
+ {
+ zlog_notice("Terminating on signal");
+ ...
+ nb_oper_cancel_all_walks();
+ mgmt_be_client_destroy(mgmt_be_client);
+ mgmt_be_client = NULL;
+
+
+Back-End XPATH mappings
+^^^^^^^^^^^^^^^^^^^^^^^
+
+In order for ``mgmtd`` to direct configuration to your daemon you need to add
+some XPATH mappings to ``mgmtd/mgmt_be_adapter.c``. These XPATHs determine which
+configuration changes get sent over the *back-end* interface to your daemon.
+There are 2 arrays to update the first for config support and the second for
+operational state.
+
+Below are the strings added for staticd config support:
+
+.. code-block:: c
+
+ #if HAVE_STATICD
+ static const char *const staticd_xpaths[] = {
+ "/frr-vrf:lib",
+ "/frr-interface:lib",
+ "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd",
+ NULL,
+ };
+ #endif
+
+ static const char *const *be_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
+ #ifdef HAVE_STATICD
+ [MGMTD_BE_CLIENT_ID_STATICD] = staticd_xpaths,
+ #endif
+ };
+
+Below are the strings added for zebra operational state support (note zebra is
+not conditionalized b/c it should always be present):
+
+.. code-block:: c
+
+ static const char *const zebra_oper_xpaths[] = {
+ "/frr-interface:lib/interface",
+ "/frr-vrf:lib/vrf/frr-zebra:zebra",
+ "/frr-zebra:zebra",
+ NULL,
+ };
+
+ static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
+ [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths,
+ };
+
+MGMTD Internals
+---------------
+
+This section will describe the internal functioning of ``mgmtd``, for now a
+couple diagrams are included to aide in source code perusal.
+
+
+The client side of a CLI configuration change
+
+.. figure:: ../figures/cli-change-client.svg
+ :align: center
+
+
+The server (mgmtd) side of a CLI configuration change
+
+.. figure:: ../figures/cli-change-mgmtd.svg
+ :align: center
+
+
+The client and server sides of oper-state query
+
+.. figure:: ../figures/cli-oper-state.svg
+ :align: center
diff --git a/doc/developer/northbound/advanced-topics.rst b/doc/developer/northbound/advanced-topics.rst
new file mode 100644
index 0000000000..eb756026e0
--- /dev/null
+++ b/doc/developer/northbound/advanced-topics.rst
@@ -0,0 +1,301 @@
+Advanced Topics
+===============
+
+.. contents:: Table of contents
+ :local:
+ :backlinks: entry
+ :depth: 1
+
+Auto-generated CLI commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to have less code to maintain, it should be possible to write a
+tool that auto-generates CLI commands based on the FRR YANG models. As a
+matter of fact, there are already a number of NETCONF-based CLIs that do
+exactly that (e.g. `Clixon <https://github.com/clicon/clixon>`__).
+
+The problem however is that there isn’t an exact one-to-one mapping
+between the existing CLI commands and the corresponding YANG nodes from
+the native models. As an example, ripd’s
+``timers basic (5-2147483647) (5-2147483647) (5-2147483647)`` command
+changes three YANG leaves at the same time. In order to auto-generate
+CLI commands and retain their original form, it’s necessary to add
+annotations in the YANG modules to specify how the commands should look
+like. Without YANG annotations, the CLI auto-generator will generate a
+command for each YANG leaf, (leaf-)list and presence-container. The
+ripd’s ``timers basic`` command, for instance, would become three
+different commands, which would be undesirable.
+
+The good news is that *libyang* allows users to create plugins to
+implement their own YANG extensions, which can be used to implement CLI
+annotations. If done properly, a CLI generator can save FRR developers
+from writing and maintaining hundreds if not thousands of DEFPYs!
+
+CLI on a separate program
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The flexible design of the northbound architecture opens the door to
+move the CLI to a separate program in the long-term future. Some
+advantages of doing so would be:
+
+* Treat the CLI as just another northbound client, instead of having CLI
+ commands embedded in the binaries of all FRR daemons.
+
+* Improved robustness: bugs in CLI commands (e.g. null-pointer dereferences) or
+ in the CLI code itself wouldn’t affect the FRR daemons.
+
+* Foster innovation by allowing other CLI programs to be implemented, possibly
+ using higher level programming languages.
+
+The problem, however, is that the northbound retrofitting process will
+convert only the CLI configuration commands and EXEC commands in a first
+moment. Retrofitting the “show” commands is a completely different story
+and shouldn’t happen anytime soon. This should hinder progress towards
+moving the CLI to a separate program.
+
+Proposed feature: confirmed commits
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Confirmed commits allow the user to request an automatic rollback to the
+previous configuration if the commit operation is not confirmed within a
+number of minutes. This is particularly useful when the user is
+accessing the CLI through the network (e.g. using SSH) and any
+configuration change might cause an unexpected loss of connectivity
+between the user and the router (e.g. misconfiguration of a routing
+protocol). By using a confirmed commit, the user can rest assured the
+connectivity will be restored after the given timeout expires, avoiding
+the need to access the router physically to fix the problem.
+
+Example of how this feature could be provided in the CLI:
+``commit confirmed [minutes <1-60>]``. The ability to do confirmed
+commits should also be exposed in the northbound API so that the
+northbound plugins can also take advantage of it (in the case of the
+Sysrepo plugin, confirmed commit is implemented externally in the
+*netopeer2-server* daemon).
+
+Proposed feature: enable/disable configuration commands/sections
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Since the ``lyd_node`` data structure from *libyang* can hold private
+data, it should be possible to mark configuration commands or sections
+as active or inactive. This would allow CLI users to leverage this
+feature to disable parts of the running configuration without actually
+removing the associated commands, and then re-enable the disabled
+configuration commands or sections later when necessary. Example:
+
+::
+
+ ripd(config)# show configuration running
+ Configuration:
+ [snip]
+ !
+ router rip
+ default-metric 2
+ distance 80
+ network eth0
+ network eth1
+ !
+ end
+ ripd(config)# disable router rip
+ ripd(config)# commit
+ % Configuration committed successfully (Transaction ID #7).
+
+ ripd(config)# show configuration running
+ Configuration:
+ [snip]
+ !
+ !router rip
+ !default-metric 2
+ !distance 80
+ !network eth0
+ !network eth1
+ !
+ end
+ ripd(config)# enable router rip
+ ripd(config)# commit
+ % Configuration committed successfully (Transaction ID #8).
+
+ ripd(config)# show configuration running
+ [snip]
+ frr defaults traditional
+ !
+ router rip
+ default-metric 2
+ distance 80
+ network eth0
+ network eth1
+ !
+ end
+
+This capability could be useful in a number of occasions, like disabling
+configuration commands that are no longer necessary (e.g. ACLs) but that
+might be necessary at a later point in the future. Other example is
+allowing users to disable a configuration section for testing purposes,
+and then re-enable it easily without needing to copy and paste any
+command.
+
+Configuration reloads
+~~~~~~~~~~~~~~~~~~~~~
+
+Given the limitations of the previous northbound architecture, the FRR
+daemons didn’t have the ability to reload their configuration files by
+themselves. The SIGHUP handler of most daemons would only re-read the
+configuration file and merge it into the running configuration. In most
+cases, however, what is desired is to replace the running configuration
+by the updated configuration file. The *frr-reload.py* script was
+written to work around this problem and it does it well to a certain
+extent. The problem with the *frr-reload.py* script is that it’s full of
+special cases here and there, which makes it fragile and unreliable.
+Maintaining the script is also an additional burden for FRR developers,
+few of whom are familiar with its code or know when it needs to be
+updated to account for a new feature.
+
+In the new northbound architecture, reloading the configuration file can
+be easily implemented using a configuration transaction. Once the FRR
+northbound retrofitting process is complete, all daemons should have the
+ability to reload their configuration files upon receiving the SIGHUP
+signal, or when the ``configuration load [...] replace`` command is
+used. Once that point is reached, the *frr-reload.py* script will no
+longer be necessary and should be removed from the FRR repository.
+
+Configuration changes coming from the kernel
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This
+`post <http://discuss.tail-f.com/t/who-should-not-set-configuration-once-a-system-is-up-and-running/111>`__
+from the Tail-f’s® forum describes the problem of letting systems
+configure themselves behind the users back. Here are some selected
+snippets from it: > Traditionally, northbound interface users are the
+ones in charge of providing configuration data for systems. > > In some
+systems, we see a deviation from this traditional practice; allowing
+systems to configure “themselves” behind the scenes (or behind the users
+back). > > While there might be a business case for such a practice,
+this kind of configuration remains “dangerous” from northbound users
+perspective and makes systems hard to predict and even harder to debug.
+(…) > > With the advent of transactional Network configuration, this
+practice can not work anymore. The fact that systems are given the right
+to change configuration is a key here in breaking transactional
+configuration in a Network.
+
+FRR is immune to some of the problems described in the aforementioned
+post. Management clients can configure interfaces that don’t yet exist,
+and once an interface is deleted from the kernel, its configuration is
+retained in FRR.
+
+There are however some cases where information learned from the kernel
+(e.g. using netlink) can affect the running configuration of all FRR
+daemons. Examples: interface rename events, VRF rename events, interface
+being moved to a different VRF, etc. In these cases, since these events
+can’t be ignored, the best we can do is to send YANG notifications to
+the management clients to inform about the configuration changes. The
+management clients should then be prepared to handle such notifications
+and react accordingly.
+
+Interfaces and VRFs
+~~~~~~~~~~~~~~~~~~~
+
+As of now zebra doesn’t have the ability to create VRFs or virtual
+interfaces in the kernel. The ``vrf`` and ``interface`` commands only
+create pre-provisioned VRFs and interfaces that are only activated when
+the corresponding information is learned from the kernel. When
+configuring FRR using an external management client, like a NETCONF
+client, it might be desirable to actually create functional VRFs and
+virtual interfaces (e.g. VLAN subinterfaces, bridges, etc) that are
+installed in the kernel using OS-specific APIs (e.g. netlink, routing
+socket, etc). Work needs to be done in this area to make this possible.
+
+Shared configuration objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+One of the existing problems in FRR is that it’s hard to ensure that all
+daemons are in sync with respect to the shared configuration objects
+(e.g. interfaces, VRFs, route-maps, ACLs, etc). When a route-map is
+configured using *vtysh*, the same command is sent to all relevant
+daemons (the daemons that implement route-maps), which ensures
+synchronization among them. The problem is when a daemon starts after
+the route-maps are created. In this case this daemon wouldn’t be aware
+of the previously configured route-maps (unlike the other daemons),
+which can lead to a lot of confusion and unexpected problems.
+
+With the new northbound architecture, configuration objects can be
+manipulated using higher level abstractions, which opens more
+possibilities to solve this decades-long problem. As an example, one
+solution would be to make the FRR daemons fetch the shared configuration
+objects from zebra using the ZAPI interface during initialization. The
+shared configuration objects could be requested using a list of XPaths
+expressions in the ``ZEBRA_HELLO`` message, which zebra would respond by
+sending the shared configuration objects encoded in the JSON format.
+This solution however doesn’t address the case where zebra starts or
+restarts after the other FRR daemons. Other solution would be to store
+the shared configuration objects in the northbound SQL database and make
+all daemons fetch these objects from there. So far no work has been made
+on this area as more investigation needs to be done.
+
+vtysh support
+~~~~~~~~~~~~~
+
+As explained in the [[Transactional CLI]] page, all commands introduced
+by the transactional CLI are not yet available in *vtysh*. This needs to
+be addressed in the short term future. Some challenges for doing that
+work include:
+
+* How to display configurations (running, candidates and rollbacks) in a more
+ clever way? The implementation of the ``show running-config`` command in
+ *vtysh* is not something that should be followed as an example. A better idea
+ would be to fetch the desired configuration from all daemons (encoded in JSON
+ for example), merge them all into a single ``lyd_node`` variable and then
+ display the combined configurations from this variable (the configuration
+ merges would transparently take care of combining the shared configuration
+ objects). In order to be able to manipulate the JSON configurations, *vtysh*
+ will need to load the YANG modules from all daemons at startup (this might
+ have a minimal impact on startup time). The only issue with this approach is
+ that the ``cli_show()`` callbacks from all daemons are embedded in their
+ binaries and thus not accessible externally. It might be necessary to compile
+ these callbacks on a separate shared library so that they are accessible to
+ *vtysh* too. Other than that, displaying the combined configurations in the
+ JSON/XML formats should be straightforward.
+
+* With the current design, transaction IDs are per-daemon and not global across
+ all FRR daemons. This means that the same transaction ID can represent
+ different transactions on different daemons. Given this observation, how to
+ implement the ``rollback configuration`` command in *vtysh*? The easy solution
+ would be to add a ``daemon WORD`` argument to specify the context of the
+ rollback, but per-daemon rollbacks would certainly be confusing and convoluted
+ to end users. A better idea would be to attack the root of the problem: change
+ configuration transactions to be global instead of being per-daemon. This
+ involves a bigger change in the northbound architecture, and would have
+ implications on how transactions are stored in the SQL database
+ (daemon-specific and shared configuration objects would need to have their own
+ tables or columns).
+
+* Loading configuration files in the JSON or XML formats will be tricky, as
+ *vtysh* will need to know which sections of the configuration should be sent
+ to which daemons. *vtysh* will either need to fetch the YANG modules
+ implemented by all daemons at runtime or obtain this information at
+ compile-time somehow.
+
+Detecting type mismatches at compile-time
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As described in the [[Retrofitting Configuration Commands]] page, the
+northbound configuration callbacks detect type mismatches at runtime
+when fetching data from the the ``dnode`` parameter (which represents
+the configuration node being created, modified, deleted or moved). When
+a type mismatch is detected, the program aborts and displays a backtrace
+showing where the problem happened. It would be desirable to detect such
+type mismatches at compile-time, the earlier the problems are detected
+the sooner they are fixed.
+
+One possible solution to this problem would be to auto-generate C
+structures from the YANG models and provide a function that converts a
+libyang’s ``lyd_node`` variable to a C structure containing the same
+information. The northbound callbacks could then fetch configuration
+data from this C structure, which would naturally lead to type
+mismatches being detected at compile time. One of the challenges of
+doing this would be the handling of YANG lists and leaf-lists. It would
+be necessary to use dynamic data structures like hashes or rb-trees to
+hold all elements of the lists and leaf-lists, and the process of
+converting a ``lyd_node`` to an auto-generated C-structure could be
+expensive. At this point it’s unclear if it’s worth adding more
+complexity in the northbound architecture to solve this specific
+problem.
diff --git a/doc/developer/northbound/architecture.rst b/doc/developer/northbound/architecture.rst
new file mode 100644
index 0000000000..4e84f1d6a4
--- /dev/null
+++ b/doc/developer/northbound/architecture.rst
@@ -0,0 +1,282 @@
+Architecture
+============
+
+Introduction
+------------
+
+The goal of the new northbound API is to provide a better interface to
+configure and monitor FRR programatically. The current design based on
+CLI commands is no longer adequate in a world where computer networks
+are becoming increasingly bigger, more diverse and more complex. Network
+scripting using *expect* and screen scraping techniques is too primitive
+and unreliable to be used in large-scale networks. What is proposed is
+to modernize FRR to turn it into an API-first routing stack, and
+reposition the CLI on top of this API. The most important change,
+however, is not the API that will be provided to external users. In
+fact, multiple APIs will be supported and users will have the ability to
+write custom management APIs if necessary. The biggest change is the
+introduction of a model-driven management architecture based on the
+`YANG <https://tools.ietf.org/html/rfc7950>`__ modeling language.
+Instead of writing code tied to any particular user interface
+(e.g. DEFUNs), YANG allows us to write API-agnostic code (in the form of
+callbacks) that can be used by any management interface. As an example,
+it shouldn’t matter if a set of configuration changes is coming from a
+`NETCONF <https://tools.ietf.org/html/rfc6241>`__ session or from a CLI
+terminal, the same callbacks should be called to process the
+configuration changes regardless of where they came from. This
+model-driven design ensures feature parity across all management
+interfaces supported by FRR.
+
+Quoting :rfc:`7950`:
+
+ YANG is a language originally designed to model data for the NETCONF
+ protocol. A YANG module defines hierarchies of data that can be used for
+ NETCONF-based operations, including configuration, state data, RPCs, and
+ notifications. This allows a complete description of all data sent between a
+ NETCONF client and server. Although out of scope for this specification,
+ YANG can also be used with protocols other than NETCONF.
+
+While the YANG and NETCONF specifications are tightly coupled with one
+another, both are independent to a certain extent and are evolving
+separately. Examples of other management protocols that use YANG include
+`RESTCONF <https://tools.ietf.org/html/rfc8040>`__,
+`gNMI <https://github.com/openconfig/reference/tree/master/rpc/gnmi>`__
+and
+`CoAP <https://www.ietf.org/archive/id/draft-vanderstok-core-comi-11.txt>`__.
+
+In addition to being management-protocol independent, some other
+advantages of using YANG in FRR are listed below:
+
+* Have a formal contract between FRR and application developers (management
+ clients). A management client that has access to the FRR YANG models knows
+ about all existing configuration options available for use. This information
+ can be used to auto-generate user-friendly interfaces like Web-UIs, custom
+ CLIs and even code bindings for several different programming languages. Using
+ `PyangBind <https://github.com/robshakir/pyangbind>`__, for example, it’s
+ possible to generate Python class hierarchies from YANG models and use these
+ classes to instantiate objects that mirror the structure of the YANG modules
+ and can be serialized/deserialized using different encoding formats.
+
+* Support different encoding formats for instance data. Currently only JSON and
+ XML are supported, but `GPB
+ <https://developers.google.com/protocol-buffers/>`__ and `CBOR
+ <http://cbor.io/>`__ are other viable options in the long term. Additional
+ encoding formats can be implemented in the *libyang* library for optimal
+ performance, or externally by translating data to/from one of the supported
+ formats (with a performance penalty).
+
+* Have a formal mechanism to introduce backward-incompatible changes based on
+ `semantic versioning <http://www.openconfig.net/docs/semver/>`__ (not part of
+ the YANG standard, which allows backward-compatible module updates only).
+
+* Provide seamless support to the industry-standard NETCONF/RESTCONF protocols
+ as alternative management APIs. If FRR configuration/state data is modeled
+ using YANG, supporting YANG-based protocols like NETCONF and RESTCONF is much
+ easier.
+
+As important as shifting to a model-driven management paradigm, the new
+northbound architecture also introduces the concept of configuration
+transactions. Configuration transactions allow management clients to
+commit multiple configuration changes at the same time and rest assured
+that either all changes will be applied or none will (all-or-nothing).
+Configuration transactions are implemented as pseudo-atomic operations
+and facilitate automation by removing the burden of error recovery from
+the management side. Another property of configuration transactions is
+that the configuration changes are always processed in a pre-defined
+order to ensure consistency. Configuration transactions that encompass
+multiple network devices are called network-wide transactions and are
+also supported by the new northbound architecture. When FRR is built
+using the ``--enable-config-rollbacks`` option, all committed
+transactions are recorded in the FRR rollback log, which can reside
+either in memory (volatile) or on persistent storage.
+
+ Network-wide Transactions is the most important leap in network
+ management technology since SNMP. The error recovery and sequencing
+ tasks are removed from the manager side. This is usually more than
+ half the cost in a mature system; more than the entire cost of the
+ managed devices.
+ `[source] <https://www.nanog.org/sites/default/files/tuesday_tutorial_moberg_netconf_35.pdf>`__.
+
+Figures 1 and 2 below illustrate the old and new northbound architecture
+of FRR, respectively. As it can be seen, in the old architecture the CLI
+was the only interface used to configure and monitor FRR (the SNMP
+plugin was’t taken into account given the small number of implemented
+MIBs). This means that the only way to automate FRR was by writing
+scripts that send CLI commands and parse the text output (which usually
+doesn’t have any structure) using screen scraping and regular
+expressions.
+
+.. figure:: images/arch-before.png
+ :alt: diagram of northbound architecture prior to nbapi conversion
+
+ Old northbound architecture
+
+The new northbound architectures, on the other hand, features a
+multitude of different management APIs, all of them connected to the
+northbound layer of the FRR daemons. By default, only the CLI interface
+is compiled built-in in the FRR daemons. The other management interfaces
+are provided as optional plugins and need to be loaded during the daemon
+initialization (e.g. *zebra -M grpc*). This design makes it possible to
+integrate FRR with different NETCONF solutions without introducing
+vendor lock-in. The [[Plugins - Writing Your Own]] page explains how to
+write custom northbound plugins that can be tailored to all needs
+(e.g. support custom transport protocols, different data encoding
+formats, fine-grained access control, etc).
+
+.. figure:: images/arch-after.png
+ :alt: diagram of northbound architecture after nbapi conversion
+
+ New northbound architecture
+
+Figure 3 shows the internal view of the FRR northbound architecture. In
+this image we can see that northbound layer is an abstract entity
+positioned between the northbound callbacks and the northbound clients.
+The northbound layer is responsible to process the requests coming from
+the northbound clients and call the appropriate callbacks to satisfy
+these requests. The northbound plugins communicate with the northbound
+layer through a public API, which allow users to write third-party
+plugins that can be maintained separately. The northbound plugins, in
+turn, have their own APIs to communicate with external management
+clients.
+
+.. figure:: images/nb-layer.png
+ :alt: diagram of northbound architecture internals
+
+ New northbound architecture - internal view
+
+Initially the CLI (and all of its commands) will be maintained inside
+the FRR daemons. In the long term, however, the goal is to move the CLI
+to a separate program just like any other management client. The
+[[Advanced Topics]] page describes the motivations and challenges of
+doing that. Last but not least, the *libyang* block inside the
+northbound layer is the engine that makes everything possible. The
+*libyang* library will be described in more detail in the following
+sections.
+
+YANG models
+-----------
+
+The main decision to be made when using YANG is which models to
+implement. There’s a general consensus that using standard models is
+preferable over using custom (native) models. The reasoning is that
+applications based on standard models can be reused for all network
+appliances that support those models, whereas the same doesn’t apply for
+applications written based on custom models.
+
+That said, there are multiple standards bodies publishing YANG models
+and unfortunately not all of them are converging (or at least not yet).
+In the context of FRR, which is a routing stack, the two sets of YANG
+models that would make sense to implement are the ones from IETF and
+from the OpenConfig working group. The question that arises is: which
+one of them should we commit to? Or should we try to support both
+somehow, at the cost of extra development efforts?
+
+Another problem, from an implementation point of view, is that it’s
+challenging to adapt the existing code base to match standard models. A
+more reasonable solution, at least in a first moment, would be to use
+YANG deviations and augmentations to do the opposite: adapt the standard
+models to the existing code. In practice however this is not as simple
+as it seems. There are cases where the differences are too substantial
+to be worked around without restructuring the code by changing its data
+structures and their relationships. As an example, the *ietf-rip* model
+places per-interface RIP configuration parameters inside the
+*control-plane-protocol* list (which is augmented by *ietf-rip*). This
+means that it’s impossible to configure RIP interface parameters without
+first configuring a RIP routing instance. The *ripd* daemon on the other
+hand allows the operator to configure RIP interface parameters even if
+``router rip`` is not configured. If we were to implement the *ietf-rip*
+module natively, we’d need to change ripd’s CLI commands (and the
+associated code) to reflect the new configuration hierarchy.
+
+Taking into account that FRR has a huge code base and that the
+northbound retrofitting process per-se will cause a lot of impact, it
+was decided to take a conservative approach and write custom YANG models
+for FRR modeled after the existing CLI commands. Having YANG models that
+closely mirror the CLI commands will allow the FRR developers to
+retrofit the code base much more easily, without introducing
+backward-incompatible changes in the CLI and reducing the likelihood of
+introducing bugs. The [[Retrofitting Configuration Commands]] page
+explains in detail how to convert configuration commands to the new
+northbound model.
+
+Even though having native YANG models is not the ideal solution, it will
+be already a big step forward for FRR to migrate to a model-driven
+management architecture, with support for configuration transactions and
+multiple management interfaces, including NETCONF and RESTCONF (through
+the northbound plugins).
+
+The new northbound also features an experimental YANG module translator
+that will allow users to translate to and from standard YANG models by
+using translation tables. The [[YANG module translator]] page describes
+this mechanism in more detail. At this point it’s unclear what can be
+achieved through module translation and if that can be considered as a
+definitive solution to support standard models or not.
+
+Northbound Architecture
+-----------------------
+
+.. figure:: images/lys-node.png
+ :alt: diagram of libyanbg's lys_node data structure
+
+ ``libyang's`` lys_node data structure
+
+
+.. figure:: images/lyd-node.png
+ :alt: diagram of libyanbg's lyd_node data structure
+
+ ``libyang's`` lyd_node data structure
+
+
+.. figure:: images/ly-ctx.png
+ :alt: diagram of libyanbg's ly_ctx data structure
+
+ ``libyang's`` ly_ctx data structure
+
+
+.. figure:: images/transactions.png
+ :alt: diagram showing how configuration transactions work
+
+ Configuration transactions
+
+
+Testing
+-------
+
+The new northbound adds the libyang library as a new mandatory
+dependency for FRR. To obtain and install this library, follow the steps
+below:
+
+.. code-block:: console
+
+ git clone https://github.com/CESNET/libyang
+ cd libyang
+ git checkout devel
+ mkdir build ; cd build
+ cmake -DENABLE_LYD_PRIV=ON ..
+ make
+ sudo make install
+
+
+.. note::
+
+ first make sure to install the libyang
+ `requirements <https://github.com/CESNET/libyang#build-requirements>`__.
+
+
+FRR needs libyang from version 0.16.7 or newer, which is maintained in
+the ``devel`` branch. libyang 0.15.x is maintained in the ``master``
+branch and doesn’t contain one small feature used by FRR (the
+``LY_CTX_DISABLE_SEARCHDIR_CWD`` flag). FRR also makes use of the
+libyang’s ``ENABLE_LYD_PRIV`` feature, which is disabled by default and
+needs to be enabled at compile time.
+
+It’s advisable (but not required) to install sqlite3 and build FRR with
+``--enable-config-rollbacks`` in order to have access to the
+configuration rollback feature.
+
+To test the northbound, the suggested method is to use the
+[[Transactional CLI]] with the *ripd* daemon and play with the new
+commands. The ``debug northbound`` command can be used to see which
+northbound callbacks are called in response to the ``commit`` command.
+For reference, the [[Demos]] page shows a small demonstration of the
+transactional CLI in action and what it’s capable of.
diff --git a/doc/developer/northbound/demos.rst b/doc/developer/northbound/demos.rst
new file mode 100644
index 0000000000..7c5ae0c229
--- /dev/null
+++ b/doc/developer/northbound/demos.rst
@@ -0,0 +1,10 @@
+Demos
+=====
+
+Transactional CLI
+-----------------
+
+This short demo shows some of the capabilities of the new transactional
+CLI:
+
+|asciicast1|
diff --git a/doc/developer/northbound/images/arch-after.png b/doc/developer/northbound/images/arch-after.png
new file mode 100644
index 0000000000..01e6ae6364
--- /dev/null
+++ b/doc/developer/northbound/images/arch-after.png
Binary files differ
diff --git a/doc/developer/northbound/images/arch-before.png b/doc/developer/northbound/images/arch-before.png
new file mode 100644
index 0000000000..ab2bb0deb2
--- /dev/null
+++ b/doc/developer/northbound/images/arch-before.png
Binary files differ
diff --git a/doc/developer/northbound/images/ly-ctx.png b/doc/developer/northbound/images/ly-ctx.png
new file mode 100644
index 0000000000..4d4e138c73
--- /dev/null
+++ b/doc/developer/northbound/images/ly-ctx.png
Binary files differ
diff --git a/doc/developer/northbound/images/lyd-node.png b/doc/developer/northbound/images/lyd-node.png
new file mode 100644
index 0000000000..4ba2b48b71
--- /dev/null
+++ b/doc/developer/northbound/images/lyd-node.png
Binary files differ
diff --git a/doc/developer/northbound/images/lys-node.png b/doc/developer/northbound/images/lys-node.png
new file mode 100644
index 0000000000..e9e46e7f64
--- /dev/null
+++ b/doc/developer/northbound/images/lys-node.png
Binary files differ
diff --git a/doc/developer/northbound/images/nb-layer.png b/doc/developer/northbound/images/nb-layer.png
new file mode 100644
index 0000000000..4aa1fd6bff
--- /dev/null
+++ b/doc/developer/northbound/images/nb-layer.png
Binary files differ
diff --git a/doc/developer/northbound/images/transactions.png b/doc/developer/northbound/images/transactions.png
new file mode 100644
index 0000000000..d18faf4478
--- /dev/null
+++ b/doc/developer/northbound/images/transactions.png
Binary files differ
diff --git a/doc/developer/northbound/links.rst b/doc/developer/northbound/links.rst
new file mode 100644
index 0000000000..e8fb327238
--- /dev/null
+++ b/doc/developer/northbound/links.rst
@@ -0,0 +1,228 @@
+Links
+=====
+
+RFCs
+~~~~
+
+- `RFC 7950 - The YANG 1.1 Data Modeling
+ Language <https://tools.ietf.org/html/rfc7950>`__
+- `RFC 7951 - JSON Encoding of Data Modeled with
+ YANG <https://tools.ietf.org/html/rfc7951>`__
+- `RFC 8342 - Network Management Datastore Architecture
+ (NMDA) <https://tools.ietf.org/html/rfc8342>`__
+- `RFC 6087 - Guidelines for Authors and Reviewers of YANG Data Model
+ Documents <https://tools.ietf.org/html/rfc6087>`__
+- `RFC 8340 - YANG Tree
+ Diagrams <https://tools.ietf.org/html/rfc8340>`__
+- `RFC 6991 - Common YANG Data
+ Types <https://tools.ietf.org/html/rfc6991>`__
+- `RFC 6241 - Network Configuration Protocol
+ (NETCONF) <https://tools.ietf.org/html/rfc6241>`__
+- `RFC 8040 - RESTCONF
+ Protocol <https://tools.ietf.org/html/rfc8040>`__
+
+YANG models
+~~~~~~~~~~~
+
+- Collection of several YANG models, including models from standards
+ organizations such as the IETF and vendor specific models:
+ https://github.com/YangModels/yang
+- OpenConfig: https://github.com/openconfig/public
+
+Presentations
+~~~~~~~~~~~~~
+
+- FRR Advanced Northbound API (May 2018)
+
+ - Slides:
+ https://www.dropbox.com/s/zhybthruwocbqaw/netdef-frr-northbound.pdf?dl=1
+
+- Ok, We Got Data Models, Now What?
+
+ - Video: https://www.youtube.com/watch?v=2oqkiZ83vAA
+ - Slides:
+ https://www.nanog.org/sites/default/files/20161017_Alvarez_Ok_We_Got_v1.pdf
+
+- Data Model-Driven Management: Latest Industry and Tool Developments
+
+ - Video: https://www.youtube.com/watch?v=n_oKGJ_jgYQ
+ - Slides:
+ https://pc.nanog.org/static/published/meetings/NANOG72/1559/20180219_Claise_Data_Modeling-Driven_Management__v1.pdf
+
+- Network Automation And Programmability: Reality Versus The Vendor
+ Hype When Considering Legacy And NFV Networks
+
+ - Video: https://www.youtube.com/watch?v=N5wbYncUS9o
+ - Slides:
+ https://www.nanog.org/sites/default/files/1_Moore_Network_Automation_And_Programmability.pdf
+
+- Lightning Talk: The API is the new CLI?
+
+ - Video: https://www.youtube.com/watch?v=ngi0erGNi58
+ - Slides:
+ https://pc.nanog.org/static/published/meetings/NANOG72/1638/20180221_Grundemann_Lightning_Talk_The_v1.pdf
+
+- Lightning Talk: OpenConfig - progress toward vendor-neutral network
+ management
+
+ - Video: https://www.youtube.com/watch?v=10rSUbeMmT4
+ - Slides:
+ https://pc.nanog.org/static/published/meetings/NANOG71/1535/20171004_Shaikh_Lightning_Talk_Openconfig_v1.pdf
+
+- Getting started with OpenConfig
+
+ - Video: https://www.youtube.com/watch?v=L7trUNK8NJI
+ - Slides:
+ https://pc.nanog.org/static/published/meetings/NANOG71/1456/20171003_Alvarez_Getting_Started_With_v1.pdf
+
+- Why NETCONF and YANG
+
+ - Video: https://www.youtube.com/watch?v=mp4h8aSTba8
+
+- NETCONF and YANG Concepts
+
+ - Video: https://www.youtube.com/watch?v=UwYYvT7DBvg
+
+- NETCONF Tutorial
+
+ - Video: https://www.youtube.com/watch?v=N4vov1mI14U
+
+Whitepapers
+~~~~~~~~~~~
+
+- Automating Network and Service Configuration Using NETCONF and YANG:
+ http://www.tail-f.com/wordpress/wp-content/uploads/2013/02/Tail-f-Presentation-Netconf-Yang.pdf
+- Creating the Programmable Network: The Business Case for NETCONF/YANG
+ in Network Devices:
+ http://www.tail-f.com/wordpress/wp-content/uploads/2013/10/HR-Tail-f-NETCONF-WP-10-08-13.pdf
+- NETCONF/YANG: What’s Holding Back Adoption & How to Accelerate It:
+ https://www.oneaccess-net.com/images/public/wp_heavy_reading.pdf
+- Achieving Automation with YANG Modeling Technologies:
+ https://www.cisco.com/c/dam/en/us/products/collateral/cloud-systems-management/network-services-orchestrator/idc-achieving-automation-wp.pdf
+
+Blog posts and podcasts
+~~~~~~~~~~~~~~~~~~~~~~~
+
+- OpenConfig and IETF YANG Models: Can they converge? -
+ http://rob.sh/post/215/
+- OpenConfig: Standardized Models For Networking -
+ https://packetpushers.net/openconfig-standardized-models-networking/
+- (Podcast) OpenConfig: From Basics to Implementations -
+ https://blog.ipspace.net/2017/02/openconfig-from-basics-to.html
+- (Podcast) How Did NETCONF Start on Software Gone Wild -
+ https://blog.ipspace.net/2017/12/how-did-netconf-start-on-software-gone.html
+- YANG Data Models in the Industry: Current State of Affairs (March
+ 2018) -
+ https://www.claise.be/2018/03/yang-data-models-in-the-industry-current-state-of-affairs-march-2018/
+- Why Data Model-driven Telemetry is the only useful Telemetry? -
+ https://www.claise.be/2018/02/why-data-model-driven-telemetry-is-the-only-useful-telemetry/
+- NETCONF versus RESTCONF: Capabilitity Comparisons for Data
+ Model-driven Management -
+ https://www.claise.be/2017/10/netconf-versus-restconf-capabilitity-comparisons-for-data-model-driven-management-2/
+- An Introduction to NETCONF/YANG -
+ https://www.fir3net.com/Networking/Protocols/an-introduction-to-netconf-yang.html
+- Network Automation and the Rise of NETCONF -
+ https://medium.com/@k.okasha/network-automation-and-the-rise-of-netconf-e96cc33fe28
+- YANG and the Road to a Model Driven Network -
+ https://medium.com/@k.okasha/yang-and-road-to-a-model-driven-network-e9e52d47148d
+
+Software
+~~~~~~~~
+
+libyang
+^^^^^^^
+
+ libyang is a YANG data modelling language parser and toolkit written
+ (and providing API) in C.
+
+- GitHub page: https://github.com/CESNET/libyang
+- Documentaion: https://netopeer.liberouter.org/doc/libyang/master/
+
+pyang
+^^^^^
+
+ pyang is a YANG validator, transformator and code generator, written
+ in python. It can be used to validate YANG modules for correctness,
+ to transform YANG modules into other formats, and to generate code
+ from the modules.
+
+- GitHub page: https://github.com/mbj4668/pyang
+- Documentaion: https://github.com/mbj4668/pyang/wiki/Documentation
+
+ncclient
+^^^^^^^^
+
+ ncclient is a Python library that facilitates client-side scripting
+ and application development around the NETCONF protocol.
+
+- GitHub page: https://github.com/ncclient/ncclient
+- Documentaion: https://ncclient.readthedocs.io/en/latest/
+
+YDK
+^^^
+
+ ydk-gen is a developer tool that can generate API’s that are modeled
+ in YANG. Currently, it generates language binding for Python, Go and
+ C++ with planned support for other language bindings in the future.
+
+- GitHub pages:
+
+ - Generator: https://github.com/CiscoDevNet/ydk-gen
+ - Python: https://github.com/CiscoDevNet/ydk-py
+
+ - Python samples: https://github.com/CiscoDevNet/ydk-py-samples
+
+ - Go: https://github.com/CiscoDevNet/ydk-go
+ - C++: https://github.com/CiscoDevNet/ydk-cpp
+
+- Documentation:
+
+ - Python: http://ydk.cisco.com/py/docs/
+ - Go: http://ydk.cisco.com/go/docs/
+ - C++: http://ydk.cisco.com/cpp/docs/
+
+- (Blog post) Simplifying Network Programmability with Model-Driven
+ APIs:
+ https://blogs.cisco.com/sp/simplifying-network-programmability-with-model-driven-apis
+- (Video introduction) Infrastructure as a Code Using YANG, OpenConfig
+ and YDK: https://www.youtube.com/watch?v=G1b6vJW1R5w
+
+pyangbind
+^^^^^^^^^
+
+ A plugin for pyang that creates Python bindings for a YANG model.
+
+- GitHub page: https://github.com/robshakir/pyangbind
+- Documentation: http://pynms.io/pyangbind/
+
+Sysrepo
+^^^^^^^
+
+ Sysrepo is an YANG-based configuration and operational state data
+ store for Unix/Linux applications.
+
+- GitHub page: https://github.com/sysrepo/sysrepo
+- Official webpage: http://www.sysrepo.org/
+- Documentation: http://www.sysrepo.org/static/doc/html/
+
+Netopeer2
+^^^^^^^^^
+
+ Netopeer2 is a set of tools implementing network configuration tools
+ based on the NETCONF Protocol. This is the second generation of the
+ toolset, originally available as the Netopeer project. Netopeer2 is
+ based on the new generation of the NETCONF and YANG libraries -
+ libyang and libnetconf2. The Netopeer server uses sysrepo as a
+ NETCONF datastore implementation.
+
+- GitHub page: https://github.com/CESNET/Netopeer2
+
+Clixon
+^^^^^^
+
+ Clixon is an automatic configuration manager where you generate
+ interactive CLI, NETCONF, RESTCONF and embedded databases with
+ transaction support from a YANG specification.
+
+- GitHub page: https://github.com/clicon/clixon
+- Project page: http://www.clicon.org/
diff --git a/doc/developer/northbound/northbound.rst b/doc/developer/northbound/northbound.rst
new file mode 100644
index 0000000000..c5f4e2f7e6
--- /dev/null
+++ b/doc/developer/northbound/northbound.rst
@@ -0,0 +1,21 @@
+.. _northbound:
+
+**************
+Northbound API
+**************
+
+.. toctree::
+ :maxdepth: 2
+
+ architecture
+ transactional-cli
+ retrofitting-configuration-commands
+ operational-data-rpcs-and-notifications
+ advanced-topics
+ yang-tools
+ yang-module-translator
+ demos
+ links
+ plugins-sysrepo
+ ppr-basic-test-topology
+ ppr-mpls-basic-test-topology
diff --git a/doc/developer/northbound/operational-data-rpcs-and-notifications.rst b/doc/developer/northbound/operational-data-rpcs-and-notifications.rst
new file mode 100644
index 0000000000..07f92c2ca0
--- /dev/null
+++ b/doc/developer/northbound/operational-data-rpcs-and-notifications.rst
@@ -0,0 +1,565 @@
+Operational Data, RPCs and Notifications
+========================================
+
+.. contents:: Table of contents
+ :local:
+ :backlinks: entry
+ :depth: 1
+
+Operational data
+~~~~~~~~~~~~~~~~
+
+Writing API-agnostic code for YANG-modeled operational data is
+challenging. Sysrepo, for instance, has completely different API to
+fetch operational data. So how can we write API-agnostic callbacks
+that can be used by both the Sysrepo plugin, and any other northbound
+client that might be written in the future?
+
+As an additional requirement, the callbacks must be designed in a way
+that makes in-place XPath filtering possible. As an example, a
+management client might want to retrieve only a subset of a large YANG
+list (e.g. a BGP table), and for optimal performance it should be
+possible to filter out the unwanted elements locally in the managed
+devices instead of returning all elements and performing the filtering
+on the management application.
+
+To meet all these requirements, the four callbacks below were introduced
+in the northbound architecture:
+
+.. code:: c
+
+ /*
+ * Operational data callback.
+ *
+ * The callback function should return the value of a specific leaf or
+ * inform if a typeless value (presence containers or leafs of type
+ * empty) exists or not.
+ *
+ * xpath
+ * YANG data path of the data we want to get
+ *
+ * list_entry
+ * pointer to list entry
+ *
+ * Returns:
+ * pointer to newly created yang_data structure, or NULL to indicate
+ * the absence of data
+ */
+ struct yang_data *(*get_elem)(const char *xpath, void *list_entry);
+
+ /*
+ * Operational data callback for YANG lists.
+ *
+ * The callback function should return the next entry in the list. The
+ * 'list_entry' parameter will be NULL on the first invocation.
+ *
+ * list_entry
+ * pointer to a list entry
+ *
+ * Returns:
+ * pointer to the next entry in the list, or NULL to signal that the
+ * end of the list was reached
+ */
+ void *(*get_next)(void *list_entry);
+
+ /*
+ * Operational data callback for YANG lists.
+ *
+ * The callback function should fill the 'keys' parameter based on the
+ * given list_entry.
+ *
+ * list_entry
+ * pointer to a list entry
+ *
+ * keys
+ * structure to be filled based on the attributes of the provided
+ * list entry
+ *
+ * Returns:
+ * NB_OK on success, NB_ERR otherwise
+ */
+ int (*get_keys)(void *list_entry, struct yang_list_keys *keys);
+
+ /*
+ * Operational data callback for YANG lists.
+ *
+ * The callback function should return a list entry based on the list
+ * keys given as a parameter.
+ *
+ * keys
+ * structure containing the keys of the list entry
+ *
+ * Returns:
+ * a pointer to the list entry if found, or NULL if not found
+ */
+ void *(*lookup_entry)(struct yang_list_keys *keys);
+
+These callbacks were designed to provide maximum flexibility. Each
+callback does one and only one task, they are indivisible primitives
+that can be combined in several different ways to iterate over operational
+data. The extra flexibility certainly has a performance cost, but it’s the
+price to pay if we want to expose FRR operational data using several
+different management interfaces (e.g. Sysrepo+Netopeer2). In the
+future it might be possible to introduce optional callbacks that do
+things like returning multiple objects at once. They would provide
+enhanced performance when iterating over large lists, but their use
+would be limited by the northbound plugins that can be integrated with
+them.
+
+The [[Plugins - Writing Your Own]] page explains how the northbound
+plugins can fetch operational data using the aforementioned northbound
+callbacks, and how in-place XPath filtering can be implemented.
+
+Example
+^^^^^^^
+
+Now let’s move to an example to show how these callbacks are implemented
+in practice. The following YANG container is part of the *ietf-rip*
+module and contains operational data about RIP neighbors:
+
+.. code:: yang
+
+ container neighbors {
+ description
+ "Neighbor information.";
+ list neighbor {
+ key "address";
+ description
+ "A RIP neighbor.";
+ leaf address {
+ type inet:ipv4-address;
+ description
+ "IP address that a RIP neighbor is using as its
+ source address.";
+ }
+ leaf last-update {
+ type yang:date-and-time;
+ description
+ "The time when the most recent RIP update was
+ received from this neighbor.";
+ }
+ leaf bad-packets-rcvd {
+ type yang:counter32;
+ description
+ "The number of RIP invalid packets received from
+ this neighbor which were subsequently discarded
+ for any reason (e.g. a version 0 packet, or an
+ unknown command type).";
+ }
+ leaf bad-routes-rcvd {
+ type yang:counter32;
+ description
+ "The number of routes received from this neighbor,
+ in valid RIP packets, which were ignored for any
+ reason (e.g. unknown address family, or invalid
+ metric).";
+ }
+ }
+ }
+
+We know that this is operational data because the ``neighbors``
+container is within the ``state`` container, which has the
+``config false;`` property (which is applied recursively).
+
+As expected, the ``gen_northbound_callbacks`` tool also generates
+skeleton callbacks for nodes that represent operational data:
+
+.. code:: c
+
+ {
+ .xpath = "/frr-ripd:ripd/state/neighbors/neighbor",
+ .cbs.get_next = ripd_state_neighbors_neighbor_get_next,
+ .cbs.get_keys = ripd_state_neighbors_neighbor_get_keys,
+ .cbs.lookup_entry = ripd_state_neighbors_neighbor_lookup_entry,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/state/neighbors/neighbor/address",
+ .cbs.get_elem = ripd_state_neighbors_neighbor_address_get_elem,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/state/neighbors/neighbor/last-update",
+ .cbs.get_elem = ripd_state_neighbors_neighbor_last_update_get_elem,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/state/neighbors/neighbor/bad-packets-rcvd",
+ .cbs.get_elem = ripd_state_neighbors_neighbor_bad_packets_rcvd_get_elem,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/state/neighbors/neighbor/bad-routes-rcvd",
+ .cbs.get_elem = ripd_state_neighbors_neighbor_bad_routes_rcvd_get_elem,
+ },
+
+The ``/frr-ripd:ripd/state/neighbors/neighbor`` list within the
+``neighbors`` container has three different callbacks that need to be
+implemented. Let’s start with the first one, the ``get_next`` callback:
+
+.. code:: c
+
+ static void *ripd_state_neighbors_neighbor_get_next(void *list_entry)
+ {
+ struct listnode *node;
+
+ if (list_entry == NULL)
+ node = listhead(peer_list);
+ else
+ node = listnextnode((struct listnode *)list_entry);
+
+ return node;
+ }
+
+Given a list entry, the job of this callback is to find the next element
+from the list. When the ``list_entry`` parameter is NULL, then the first
+element of the list should be returned.
+
+*ripd* uses the ``rip_peer`` structure to represent RIP neighbors, and
+the ``peer_list`` global variable (linked list) is used to store all RIP
+neighbors.
+
+In order to be able to iterate over the list of RIP neighbors, the
+callback returns a ``listnode`` variable instead of a ``rip_peer``
+variable. The ``listnextnode`` macro can then be used to find the next
+element from the linked list.
+
+Now the second callback, ``get_keys``:
+
+.. code:: c
+
+ static int ripd_state_neighbors_neighbor_get_keys(void *list_entry,
+ struct yang_list_keys *keys)
+ {
+ struct listnode *node = list_entry;
+ struct rip_peer *peer = listgetdata(node);
+
+ keys->num = 1;
+ (void)inet_ntop(AF_INET, &peer->addr, keys->key[0].value,
+ sizeof(keys->key[0].value));
+
+ return NB_OK;
+ }
+
+This one is easy. First, we obtain the RIP neighbor from the
+``listnode`` structure. Then, we fill the ``keys`` parameter according
+to the attributes of the RIP neighbor. In this case, the ``neighbor``
+YANG list has only one key: the neighbor IP address. We then use the
+``inet_ntop()`` function to transform this binary IP address into a
+string (the lingua franca of the FRR northbound).
+
+The last callback for the ``neighbor`` YANG list is the ``lookup_entry``
+callback:
+
+.. code:: c
+
+ static void *
+ ripd_state_neighbors_neighbor_lookup_entry(struct yang_list_keys *keys)
+ {
+ struct in_addr address;
+
+ yang_str2ipv4(keys->key[0].value, &address);
+
+ return rip_peer_lookup(&address);
+ }
+
+This callback is the counterpart of the ``get_keys`` callback: given an
+array of list keys, the associated list entry should be returned. The
+``yang_str2ipv4()`` function is used to convert the list key (an IP
+address) from a string to an ``in_addr`` structure. Then the
+``rip_peer_lookup()`` function is used to find the list entry.
+
+Finally, each YANG leaf inside the ``neighbor`` list has its associated
+``get_elem`` callback:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:ripd/state/neighbors/neighbor/address
+ */
+ static struct yang_data *
+ ripd_state_neighbors_neighbor_address_get_elem(const char *xpath,
+ void *list_entry)
+ {
+ struct rip_peer *peer = list_entry;
+
+ return yang_data_new_ipv4(xpath, &peer->addr);
+ }
+
+ /*
+ * XPath: /frr-ripd:ripd/state/neighbors/neighbor/last-update
+ */
+ static struct yang_data *
+ ripd_state_neighbors_neighbor_last_update_get_elem(const char *xpath,
+ void *list_entry)
+ {
+ /* TODO: yang:date-and-time is tricky */
+ return NULL;
+ }
+
+ /*
+ * XPath: /frr-ripd:ripd/state/neighbors/neighbor/bad-packets-rcvd
+ */
+ static struct yang_data *
+ ripd_state_neighbors_neighbor_bad_packets_rcvd_get_elem(const char *xpath,
+ void *list_entry)
+ {
+ struct rip_peer *peer = list_entry;
+
+ return yang_data_new_uint32(xpath, peer->recv_badpackets);
+ }
+
+ /*
+ * XPath: /frr-ripd:ripd/state/neighbors/neighbor/bad-routes-rcvd
+ */
+ static struct yang_data *
+ ripd_state_neighbors_neighbor_bad_routes_rcvd_get_elem(const char *xpath,
+ void *list_entry)
+ {
+ struct rip_peer *peer = list_entry;
+
+ return yang_data_new_uint32(xpath, peer->recv_badroutes);
+ }
+
+These callbacks receive the list entry as parameter and return the
+corresponding data using the ``yang_data_new_*()`` wrapper functions.
+Not much to explain here.
+
+Iterating over operational data without blocking the main pthread
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+One of the problems we have in FRR is that some “show” commands in the
+CLI can take too long, potentially long enough to the point of
+triggering some protocol timeouts and bringing sessions down.
+
+To avoid this kind of problem, northbound clients are encouraged to do
+one of the following:
+
+* Create a separate pthread for handling requests to fetch operational data.
+
+* Iterate over YANG lists and leaf-lists asynchronously, returning a maximum
+ number of elements per time instead of returning all elements in one shot.
+
+In order to handle both cases correctly, the ``get_next`` callbacks need
+to use locks to prevent the YANG lists from being modified while they
+are being iterated over. If that is not done, the list entry returned by
+this callback can become a dangling pointer when used in another
+callback.
+
+Currently the Sysrepo plugin runs only in the main pthread. The plan in the
+short-term is to introduce a separate pthread only for handling operational
+data, and use the main pthread only for handling configuration changes,
+RPCs and notifications.
+
+RPCs and Actions
+~~~~~~~~~~~~~~~~
+
+The FRR northbound supports YANG RPCs and Actions through the ``rpc()``
+callback, which is documented as follows in the *lib/northbound.h* file:
+
+.. code:: c
+
+ /*
+ * RPC and action callback.
+ *
+ * Both 'input' and 'output' are lists of 'yang_data' structures. The
+ * callback should fetch all the input parameters from the 'input' list,
+ * and add output parameters to the 'output' list if necessary.
+ *
+ * xpath
+ * xpath of the YANG RPC or action
+ *
+ * input
+ * read-only list of input parameters
+ *
+ * output
+ * list of output parameters to be populated by the callback
+ *
+ * Returns:
+ * NB_OK on success, NB_ERR otherwise
+ */
+ int (*rpc)(const char *xpath, const struct list *input,
+ struct list *output);
+
+Note that the same callback is used for both RPCs and actions, which are
+essentially the same thing. In the case of YANG actions, the ``xpath``
+parameter can be consulted to find the data node associated to the
+operation.
+
+As part of the northbound retrofitting process, it’s suggested to model
+some EXEC-level commands using YANG so that their functionality is
+exposed to other management interfaces other than the CLI. As an
+example, if the ``clear bgp`` command is modeled using a YANG RPC, and a
+corresponding ``rpc`` callback is written, then it should be possible to
+clear BGP neighbors using NETCONF and RESTCONF with that RPC (the Sysrepo
+plugin has full support for YANG RPCs and actions).
+
+Here’s an example of a very simple RPC modeled using YANG:
+
+.. code:: yang
+
+ rpc clear-rip-route {
+ description
+ "Clears RIP routes from the IP routing table and routes
+ redistributed into the RIP protocol.";
+ }
+
+This RPC doesn’t have any input or output parameters. Below we can see
+the implementation of the corresponding ``rpc`` callback, whose skeleton
+was automatically generated by the ``gen_northbound_callbacks`` tool:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:clear-rip-route
+ */
+ static int clear_rip_route_rpc(const char *xpath, const struct list *input,
+ struct list *output)
+ {
+ struct route_node *rp;
+ struct rip_info *rinfo;
+ struct list *list;
+ struct listnode *listnode;
+
+ /* Clear received RIP routes */
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ list = rp->info;
+ if (list == NULL)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ if (!rip_route_rte(rinfo))
+ continue;
+
+ if (CHECK_FLAG(rinfo->flags, RIP_RTF_FIB))
+ rip_zebra_ipv4_delete(rp);
+ break;
+ }
+
+ if (rinfo) {
+ RIP_TIMER_OFF(rinfo->t_timeout);
+ RIP_TIMER_OFF(rinfo->t_garbage_collect);
+ listnode_delete(list, rinfo);
+ rip_info_free(rinfo);
+ }
+
+ if (list_isempty(list)) {
+ list_delete_and_null(&list);
+ rp->info = NULL;
+ route_unlock_node(rp);
+ }
+ }
+
+ return NB_OK;
+ }
+
+If the ``clear-rip-route`` RPC had any input parameters, they would be
+available in the ``input`` list given as a parameter to the callback.
+Similarly, the ``output`` list can be used to append output parameters
+generated by the RPC, if any are defined in the YANG model.
+
+The northbound clients (CLI and northbound plugins) have the
+responsibility to create and delete the ``input`` and ``output`` lists.
+However, in the cases where the RPC or action doesn’t have any input or
+output parameters, the northbound client can pass NULL pointers to the
+``rpc`` callback to avoid creating linked lists unnecessarily. We can
+see this happening in the example below:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:clear-rip-route
+ */
+ DEFPY (clear_ip_rip,
+ clear_ip_rip_cmd,
+ "clear ip rip",
+ CLEAR_STR
+ IP_STR
+ "Clear IP RIP database\n")
+ {
+ return nb_cli_rpc("/frr-ripd:clear-rip-route", NULL, NULL);
+ }
+
+``nb_cli_rpc()`` is a helper function that merely finds the appropriate
+``rpc`` callback based on the XPath provided in the first argument, and
+map the northbound error code from the ``rpc`` callback to a vty error
+code (e.g. ``CMD_SUCCESS``, ``CMD_WARNING``). The second and third
+arguments provided to the function refer to the ``input`` and ``output``
+lists. In this case, both arguments are set to NULL since the YANG RPC
+in question doesn’t have any input/output parameters.
+
+Notifications
+~~~~~~~~~~~~~
+
+YANG notifations are sent using the ``nb_notification_send()`` function,
+documented in the *lib/northbound.h* file as follows:
+
+.. code:: c
+
+ /*
+ * Send a YANG notification. This is a no-op unless the 'nb_notification_send'
+ * hook was registered by a northbound plugin.
+ *
+ * xpath
+ * xpath of the YANG notification
+ *
+ * arguments
+ * linked list containing the arguments that should be sent. This list is
+ * deleted after being used.
+ *
+ * Returns:
+ * NB_OK on success, NB_ERR otherwise
+ */
+ extern int nb_notification_send(const char *xpath, struct list *arguments);
+
+The northbound doesn’t use callbacks for notifications because
+notifications are generated locally and sent to the northbound clients.
+This way, whenever a notification needs to be sent, it’s possible to
+call the appropriate function directly instead of finding a callback
+based on the XPath of the YANG notification.
+
+As an example, the *ietf-rip* module contains the following
+notification:
+
+.. code:: yang
+
+ notification authentication-failure {
+ description
+ "This notification is sent when the system
+ receives a PDU with the wrong authentication
+ information.";
+ leaf interface-name {
+ type string;
+ description
+ "Describes the name of the RIP interface.";
+ }
+ }
+
+The following convenience function was implemented in *ripd* to send
+*authentication-failure* YANG notifications:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:authentication-failure
+ */
+ void ripd_notif_send_auth_failure(const char *ifname)
+ {
+ const char *xpath = "/frr-ripd:authentication-failure";
+ struct list *arguments;
+ char xpath_arg[XPATH_MAXLEN];
+ struct yang_data *data;
+
+ arguments = yang_data_list_new();
+
+ snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath);
+ data = yang_data_new_string(xpath_arg, ifname);
+ listnode_add(arguments, data);
+
+ nb_notification_send(xpath, arguments);
+ }
+
+Now sending the *authentication-failure* YANG notification should be as
+simple as calling the above function and provide the appropriate
+interface name. The notification will be processed by all northbound
+plugins that subscribed a callback to the ``nb_notification_send`` hook.
+The Sysrepo plugin, for instance, uses this hook to relay the notifications
+to the *sysrepod* daemon, which can generate NETCONF notifications to subscribed
+clients. When no northbound plugin is loaded, ``nb_notification_send()`` doesn’t
+do anything and the notifications are ignored.
diff --git a/doc/developer/northbound/plugins-sysrepo.rst b/doc/developer/northbound/plugins-sysrepo.rst
new file mode 100644
index 0000000000..0cfdb825e5
--- /dev/null
+++ b/doc/developer/northbound/plugins-sysrepo.rst
@@ -0,0 +1,193 @@
+Plugins Sysrepo
+===============
+
+Installation
+------------
+
+Required dependencies
+^^^^^^^^^^^^^^^^^^^^^
+Install FRR build required dependencies, check `Building FRR
+<https://docs.frrouting.org/projects/dev-guide/en/latest/building.html>`_ document for specific platform required packages.
+Below are debian systems required packages:
+
+.. code-block:: console
+
+ sudo apt-get install git autoconf automake libtool make \
+ libprotobuf-c-dev protobuf-c-compiler build-essential \
+ python3-dev python3-pytest python3-sphinx libjson-c-dev \
+ libelf-dev libreadline-dev cmake libcap-dev bison flex \
+ pkg-config texinfo gdb libgrpc-dev python3-grpc-tools libpcre2-dev
+
+libyang
+^^^^^^^
+
+.. note::
+
+ FRR requires version 2.1.128 or newer, in this document we will
+ be compiling and installing libyang version 2.1.148.
+
+.. code-block:: console
+
+ git clone https://github.com/CESNET/libyang.git
+ cd libyang
+ git checkout v2.1.148
+ mkdir build; cd build
+ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \
+ -DCMAKE_BUILD_TYPE:String="Release" ..
+ make
+ sudo make install
+
+Sysrepo
+^^^^^^^
+
+.. note::
+
+ The following code block assumes you have installed libyang v2.1.148, if you have
+ libyang v2.1.128 change sysrepo version to 2.2.105.
+
+.. code-block:: console
+
+ git clone https://github.com/sysrepo/sysrepo.git
+ cd sysrepo/
+ git checkout v2.2.150
+ mkdir build; cd build
+ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \
+ -DCMAKE_BUILD_TYPE:String="Release" ..
+ make
+ sudo make install
+
+Verify that sysrepo is installed correctly:
+
+.. code-block:: console
+
+ sudo sysrepoctl -l
+
+FRR
+^^^
+
+Follow the steps of `Building FRR
+<https://docs.frrouting.org/projects/dev-guide/en/latest/building.html>`_
+
+
+Make sure to use ``--enable-sysrepo`` configure-time option while building FRR.
+
+Below is an example of frr configure-time options, your options
+might vary, however in order to allow sysrepo plugin you have
+to keep ``--enable-sysrepo`` option:
+
+.. code-block:: console
+
+ ./bootstrap.sh
+ ./configure \
+ --localstatedir=/var/opt/frr \
+ --sbindir=/usr/lib/frr \
+ --sysconfdir=/etc/frr \
+ --enable-multipath=64 \
+ --enable-user=frr \
+ --enable-group=frr \
+ --enable-vty-group=frrvty \
+ --enable-configfile-mask=0640 \
+ --enable-logfile-mask=0640 \
+ --enable-fpm \
+ --enable-sysrepo \
+ --with-pkg-git-version \
+ --with-pkg-extra-version=-MyOwnFRRVersion
+ make
+ make check
+ sudo make install
+
+
+Initialization
+--------------
+
+Install FRR YANG modules in Sysrepo datastore:
+
+.. code-block:: console
+
+ cd frr/yang/
+ sudo sysrepoctl -i ./ietf/ietf-interfaces.yang -o frr -g frr
+ sudo sysrepoctl -i frr-vrf.yang -o frr -g frr
+ sudo sysrepoctl -i frr-interface.yang -o frr -g frr
+ sudo sysrepoctl -i frr-route-types.yang -o frr -g frr
+ sudo sysrepoctl -i frr-filter.yang -o frr -g frr
+ sudo sysrepoctl -i frr-route-map.yang -o frr -g frr
+ sudo sysrepoctl -i frr-isisd.yang -o frr -g frr
+ sudo sysrepoctl -i frr-bfdd.yang -o frr -g frr
+ sudo sysrepoctl -i ./ietf/ietf-routing-types.yang -o frr -g frr
+ sudo sysrepoctl -i frr-nexthop.yang -o frr -g frr
+ sudo sysrepoctl -i frr-if-rmap.yang -o frr -g frr
+ sudo sysrepoctl -i frr-ripd.yang -o frr -g frr
+ sudo sysrepoctl -i frr-ripngd.yang -o frr -g frr
+ sudo sysrepoctl -i frr-affinity-map.yang -o frr -g frr
+ sudo sysrepoctl -i ./ietf/frr-deviations-ietf-interfaces.yang -o frr -g frr
+
+
+Start FRR daemons with sysrepo plugin:
+
+.. code-block:: console
+
+ sudo /usr/lib/frr/isisd -M sysrepo --log stdout
+
+Any daemon running with ``-M sysrepo`` will subscribe to its frr yang moduels
+on sysrepo and you be able to configure it by editing module configuration on sysrepo.
+
+Managing the configuration
+--------------------------
+
+Testing
+^^^^^^^
+
+To test FRR intergartion with sysrepo, ``sysrepocfg`` tool can be used
+to edit frr configuration on sysrepo
+
+Example:
+
+Edit sysrepo running datastore configuration for the desiged frr module:
+
+.. code-block:: console
+
+ sudo sysrepocfg -E nano -d running -m frr-isisd -f json
+
+Paste the following json configuration:
+
+.. code-block:: console
+
+ {
+ "frr-isisd:isis": {
+ "instance": [
+ {
+ "area-tag": "testnet",
+ "vrf": "default",
+ "is-type": "level-1"
+ }
+ ]
+ }
+ }
+
+Exit and save config to the same file.
+
+After that, this configuration should get reflected to vtysh:
+
+.. code-block:: console
+
+ show run
+ Building configuration...
+
+ Current configuration:
+ !
+ frr version 9.2-dev-MyOwnFRRVersion
+ frr defaults traditional
+ hostname bullseye
+ !
+ router isis testnet
+ is-type level-1
+ exit
+ !
+ end
+
+NETCONF
+^^^^^^^
+
+To manage sysrepo configuration through netconf
+you can use `netopeer2 <https://github.com/CESNET/netopeer2>`_ as a netfconf server that can
+be easily integrated with sysrepo.
diff --git a/doc/developer/northbound/ppr-basic-test-topology.rst b/doc/developer/northbound/ppr-basic-test-topology.rst
new file mode 100644
index 0000000000..4929c9b285
--- /dev/null
+++ b/doc/developer/northbound/ppr-basic-test-topology.rst
@@ -0,0 +1,1627 @@
+IS-IS PPR Basic
+===============
+
+.. contents:: Table of contents
+ :local:
+ :backlinks: entry
+ :depth: 2
+
+Software
+~~~~~~~~
+
+The FRR PPR implementation for IS-IS is available here:
+https://github.com/opensourcerouting/frr/tree/isisd-ppr
+
+Topology
+~~~~~~~~
+
+In this topology we have an IS-IS network consisting of 12 routers. CE1
+and CE2 are the consumer edges, connected to R11 and R14, respectively.
+Three hosts are connected to the CEs using only static routes.
+
+Router R11 advertises 6 PPR TLVs, which corresponds to three
+bi-directional GRE tunnels: \* **6000:1::1 <-> 6000:2::1:** {R11 - R21 -
+R22 - R23 - R14} (IPv6 Node Addresses only) \* **6000:1::2 <->
+6000:2::2:** {R11 - R21 - R32 - R41 - R33 - R23 - R14} (IPv6 Node and
+Interface Addresses) \* **6000:1::3 <-> 6000:2::3:** {R11 - R21 - R99 -
+R23 - R14} (misconfigured path)
+
+PBR rules are configured on R11 and R14 to route the traffic between
+Host 1 and Host 3 using the first PPR tunnel. Traffic between Host 2 and
+Host 3 uses the regular IS-IS shortest path.
+
+Additional information: \* Addresses in the 4000::/16 range refer to
+interface addresses, where the last hextet corresponds to the node ID.
+\* Addresses in the 5000::/16 range refer to loopback addresses, where
+the last hextet corresponds to the node ID. \* Addresses in the
+6000::/16 range refer to PPR-ID addresses.
+
+::
+
+ +-------+ +-------+ +-------+
+ | | | | | |
+ | HOST1 | | HOST2 | | HOST3 |
+ | | | | | |
+ +---+---+ +---+---+ +---+---+
+ | | |
+ |fd00:10:1::/64 | |
+ +-----+ +------+ fd00:20:1::/64|
+ | |fd00:10:2::/64 |
+ | | |
+ +-+--+--+ +---+---+
+ | | | |
+ | CE1 | | CE2 |
+ | | | |
+ +---+---+ +---+---+
+ | |
+ | |
+ |fd00:10:0::/64 fd00:20:0::/64|
+ | |
+ | |
+ +---+---+ +-------+ +-------+ +---+---+
+ | |4000:101::/64| |4000:102::/64| |4000:103::/64| |
+ | R11 +-------------+ R12 +-------------+ R13 +-------------+ R14 |
+ | | | | | | | |
+ +---+---+ +--+-+--+ +--+-+--+ +---+---+
+ | | | | | |
+ |4000:104::/64 | |4000:106::/64 | |4000:108::/64 |
+ +---------+ +--------+ +--------+ +--------+ +--------+ +---------+
+ | |4000:105::/64 | |4000:107::/64 | |4000:109::/64
+ | | | | | |
+ +--+-+--+ +--+-+--+ +--+-+--+
+ | |4000:110::/64| |4000:111::/64| |
+ | R21 +-------------+ R22 +-------------+ R23 |
+ | | | | | |
+ +--+-+--+ +--+-+--+ +--+-+--+
+ | | | | | |
+ | |4000:113::/64 | |4000:115::/64 | |4000:117::/64
+ +---------+ +--------+ +--------+ +--------+ +--------+ +---------+
+ |4000:112::/64 | |4000:114::/64 | |4000:116::/64 |
+ | | | | | |
+ +---+---+ +--+-+--+ +--+-+--+ +---+---+
+ | |4000:118::/64| |4000:119::/64| |4000:120::/64| |
+ | R31 +-------------+ R32 +-------------+ R33 +-------------+ R34 |
+ | | | | | | | |
+ +-------+ +---+---+ +---+---+ +-------+
+ | |
+ |4000:121::/64 |
+ +----------+----------+
+ |
+ |
+ +---+---+
+ | |
+ | R41 |
+ | |
+ +-------+
+
+Configuration
+~~~~~~~~~~~~~
+
+PPR TLV processing needs to be enabled on all IS-IS routers using the
+``ppr on`` command. The advertisements of all PPR TLVs is done by router
+R11.
+
+CLI configuration
+^^^^^^^^^^^^^^^^^
+
+.. code:: yaml
+
+ ---
+
+ routers:
+
+ host1:
+ links:
+ eth-ce1:
+ peer: [ce1, eth-host1]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-ce1
+ ipv6 address fd00:10:1::1/64
+ !
+ ipv6 route ::/0 fd00:10:1::100
+
+ host2:
+ links:
+ eth-ce1:
+ peer: [ce1, eth-host2]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-ce1
+ ipv6 address fd00:10:2::1/64
+ !
+ ipv6 route ::/0 fd00:10:2::100
+
+ host3:
+ links:
+ eth-ce2:
+ peer: [ce2, eth-host3]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-ce2
+ ipv6 address fd00:20:1::1/64
+ !
+ ipv6 route ::/0 fd00:20:1::100
+
+ ce1:
+ links:
+ eth-host1:
+ peer: [host1, eth-ce1]
+ eth-host2:
+ peer: [host2, eth-ce1]
+ eth-rt11:
+ peer: [rt11, eth-ce1]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-host1
+ ipv6 address fd00:10:1::100/64
+ !
+ interface eth-host2
+ ipv6 address fd00:10:2::100/64
+ !
+ interface eth-rt11
+ ipv6 address fd00:10:0::100/64
+ !
+ ipv6 route ::/0 fd00:10:0::11
+
+ ce2:
+ links:
+ eth-host3:
+ peer: [host3, eth-ce2]
+ eth-rt14:
+ peer: [rt14, eth-ce2]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-host3
+ ipv6 address fd00:20:1::100/64
+ !
+ interface eth-rt14
+ ipv6 address fd00:20:0::100/64
+ !
+ ipv6 route ::/0 fd00:20:0::14
+
+ rt11:
+ links:
+ lo-ppr:
+ eth-ce1:
+ peer: [ce1, eth-rt11]
+ eth-rt12:
+ peer: [rt12, eth-rt11]
+ eth-rt21:
+ peer: [rt21, eth-rt11]
+ shell: |
+ # GRE tunnel for preferred packets (PPR)
+ ip -6 tunnel add tun-ppr mode ip6gre remote 6000:2::1 local 6000:1::1 ttl 64
+ ip link set dev tun-ppr up
+ # PBR rules
+ ip -6 rule add from fd00:10:1::/64 to fd00:20:1::/64 iif eth-ce1 lookup 10000
+ ip -6 route add default dev tun-ppr table 10000
+ frr:
+ zebra:
+ staticd:
+ isisd:
+ config: |
+ interface lo-ppr
+ ipv6 address 6000:1::1/128
+ ipv6 address 6000:1::2/128
+ ipv6 address 6000:1::3/128
+ !
+ interface lo
+ ipv6 address 5000::11/128
+ ipv6 router isis 1
+ !
+ interface eth-ce1
+ ipv6 address fd00:10:0::11/64
+ !
+ interface eth-rt12
+ ipv6 address 4000:101::11/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt21
+ ipv6 address 4000:104::11/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ ipv6 route fd00:10::/32 fd00:10:0::100
+ !
+ ppr group VOIP
+ ppr ipv6 6000:1::1/128 prefix 5000::11/128 metric 50
+ pde ipv6-node 5000::14/128
+ pde ipv6-node 5000::23/128
+ pde ipv6-node 5000::22/128
+ pde ipv6-node 5000::21/128
+ pde ipv6-node 5000::11/128
+ !
+ ppr ipv6 6000:2::1/128 prefix 5000::14/128 metric 50
+ pde ipv6-node 5000::11/128
+ pde ipv6-node 5000::21/128
+ pde ipv6-node 5000::22/128
+ pde ipv6-node 5000::23/128
+ pde ipv6-node 5000::14/128
+ !
+ !
+ ppr group INTERFACE_PDES
+ ppr ipv6 6000:1::2/128 prefix 5000::11/128
+ pde ipv6-node 5000::14/128
+ pde ipv6-node 5000::23/128
+ pde ipv6-node 5000::33/128
+ pde ipv6-interface 4000:121::41/64
+ pde ipv6-node 5000::32/128
+ pde ipv6-interface 4000:113::21/64
+ pde ipv6-node 5000::11/128
+ !
+ ppr ipv6 6000:2::2/128 prefix 5000::14/128
+ pde ipv6-node 5000::11/128
+ pde ipv6-node 5000::21/128
+ pde ipv6-node 5000::32/128
+ pde ipv6-interface 4000:121::41/64
+ pde ipv6-node 5000::33/128
+ pde ipv6-interface 4000:116::23/64
+ pde ipv6-node 5000::14/128
+ !
+ !
+ ppr group BROKEN
+ ppr ipv6 6000:1::3/128 prefix 5000::11/128 metric 1500
+ pde ipv6-node 5000::14/128
+ pde ipv6-node 5000::23/128
+ ! non-existing node!!!
+ pde ipv6-node 5000::99/128
+ pde ipv6-node 5000::21/128
+ pde ipv6-node 5000::11/128
+ !
+ ppr ipv6 6000:2::3/128 prefix 5000::14/128 metric 1500
+ pde ipv6-node 5000::11/128
+ pde ipv6-node 5000::21/128
+ ! non-existing node!!!
+ pde ipv6-node 5000::99/128
+ pde ipv6-node 5000::23/128
+ pde ipv6-node 5000::14/128
+ !
+ !
+ router isis 1
+ net 49.0000.0000.0000.0011.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ ppr advertise VOIP
+ ppr advertise INTERFACE_PDES
+ ppr advertise BROKEN
+ !
+
+ rt12:
+ links:
+ eth-rt11:
+ peer: [rt11, eth-rt12]
+ eth-rt13:
+ peer: [rt13, eth-rt12]
+ eth-rt21:
+ peer: [rt21, eth-rt12]
+ eth-rt22:
+ peer: [rt22, eth-rt12]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::12/128
+ ipv6 router isis 1
+ !
+ interface eth-rt11
+ ipv6 address 4000:101::12/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt13
+ ipv6 address 4000:102::12/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt21
+ ipv6 address 4000:105::12/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:106::12/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0012.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt13:
+ links:
+ eth-rt12:
+ peer: [rt12, eth-rt13]
+ eth-rt14:
+ peer: [rt14, eth-rt13]
+ eth-rt22:
+ peer: [rt22, eth-rt13]
+ eth-rt23:
+ peer: [rt23, eth-rt13]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::13/128
+ ipv6 router isis 1
+ !
+ interface eth-rt12
+ ipv6 address 4000:102::13/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt14
+ ipv6 address 4000:103::13/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:107::13/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt23
+ ipv6 address 4000:108::13/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0013.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt14:
+ links:
+ lo-ppr:
+ eth-ce2:
+ peer: [ce2, eth-rt14]
+ eth-rt13:
+ peer: [rt13, eth-rt14]
+ eth-rt23:
+ peer: [rt23, eth-rt14]
+ shell: |
+ # GRE tunnel for preferred packets (PPR)
+ ip -6 tunnel add tun-ppr mode ip6gre remote 6000:1::1 local 6000:2::1 ttl 64
+ ip link set dev tun-ppr up
+ # PBR rules
+ ip -6 rule add from fd00:20:1::/64 to fd00:10:1::/64 iif eth-ce2 lookup 10000
+ ip -6 route add default dev tun-ppr table 10000
+ frr:
+ zebra:
+ staticd:
+ isisd:
+ config: |
+ interface lo-ppr
+ ipv6 address 6000:2::1/128
+ ipv6 address 6000:2::2/128
+ ipv6 address 6000:2::3/128
+ !
+ interface lo
+ ipv6 address 5000::14/128
+ ipv6 router isis 1
+ !
+ interface eth-ce2
+ ipv6 address fd00:20:0::14/64
+ !
+ interface eth-rt13
+ ipv6 address 4000:103::14/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt23
+ ipv6 address 4000:109::14/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ ipv6 route fd00:20::/32 fd00:20:0::100
+ !
+ router isis 1
+ net 49.0000.0000.0000.0014.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt21:
+ links:
+ eth-rt11:
+ peer: [rt11, eth-rt21]
+ eth-rt12:
+ peer: [rt12, eth-rt21]
+ eth-rt22:
+ peer: [rt22, eth-rt21]
+ eth-rt31:
+ peer: [rt31, eth-rt21]
+ eth-rt32:
+ peer: [rt32, eth-rt21]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::21/128
+ ipv6 router isis 1
+ !
+ interface eth-rt11
+ ipv6 address 4000:104::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt12
+ ipv6 address 4000:105::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:110::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt31
+ ipv6 address 4000:112::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt32
+ ipv6 address 4000:113::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0021.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt22:
+ links:
+ eth-rt12:
+ peer: [rt12, eth-rt22]
+ eth-rt13:
+ peer: [rt13, eth-rt22]
+ eth-rt21:
+ peer: [rt21, eth-rt22]
+ eth-rt23:
+ peer: [rt23, eth-rt22]
+ eth-rt32:
+ peer: [rt32, eth-rt22]
+ eth-rt33:
+ peer: [rt33, eth-rt22]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::22/128
+ ipv6 router isis 1
+ !
+ interface eth-rt12
+ ipv6 address 4000:106::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt13
+ ipv6 address 4000:107::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt21
+ ipv6 address 4000:110::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt23
+ ipv6 address 4000:111::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt32
+ ipv6 address 4000:114::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt33
+ ipv6 address 4000:115::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0022.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt23:
+ links:
+ eth-rt13:
+ peer: [rt13, eth-rt23]
+ eth-rt14:
+ peer: [rt14, eth-rt23]
+ eth-rt22:
+ peer: [rt22, eth-rt23]
+ eth-rt33:
+ peer: [rt33, eth-rt23]
+ eth-rt34:
+ peer: [rt34, eth-rt23]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::23/128
+ ipv6 router isis 1
+ !
+ interface eth-rt13
+ ipv6 address 4000:108::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt14
+ ipv6 address 4000:109::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:111::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt33
+ ipv6 address 4000:116::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt34
+ ipv6 address 4000:117::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0023.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt31:
+ links:
+ eth-rt21:
+ peer: [rt21, eth-rt31]
+ eth-rt32:
+ peer: [rt32, eth-rt31]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::31/128
+ ipv6 router isis 1
+ !
+ interface eth-rt21
+ ipv6 address 4000:112::31/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt32
+ ipv6 address 4000:118::31/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0031.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt32:
+ links:
+ eth-rt21:
+ peer: [rt21, eth-rt32]
+ eth-rt22:
+ peer: [rt22, eth-rt32]
+ eth-rt31:
+ peer: [rt31, eth-rt32]
+ eth-rt33:
+ peer: [rt33, eth-rt32]
+ eth-sw1:
+ peer: [sw1, eth-rt32]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::32/128
+ ipv6 router isis 1
+ !
+ interface eth-rt21
+ ipv6 address 4000:113::32/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:114::32/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt31
+ ipv6 address 4000:118::32/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt33
+ ipv6 address 4000:119::32/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-sw1
+ ipv6 address 4000:121::32/64
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0032.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt33:
+ links:
+ eth-rt22:
+ peer: [rt22, eth-rt33]
+ eth-rt23:
+ peer: [rt23, eth-rt33]
+ eth-rt32:
+ peer: [rt32, eth-rt33]
+ eth-rt34:
+ peer: [rt34, eth-rt33]
+ eth-sw1:
+ peer: [sw1, eth-rt33]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::33/128
+ ipv6 router isis 1
+ !
+ interface eth-rt22
+ ipv6 address 4000:115::33/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt23
+ ipv6 address 4000:116::33/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt32
+ ipv6 address 4000:119::33/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt34
+ ipv6 address 4000:120::33/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-sw1
+ ipv6 address 4000:121::33/64
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0033.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt34:
+ links:
+ eth-rt23:
+ peer: [rt23, eth-rt34]
+ eth-rt33:
+ peer: [rt33, eth-rt34]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::34/128
+ ipv6 router isis 1
+ !
+ interface eth-rt23
+ ipv6 address 4000:117::34/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt33
+ ipv6 address 4000:120::34/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0034.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ rt41:
+ links:
+ eth-sw1:
+ peer: [sw1, eth-rt41]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ipv6 address 5000::41/128
+ ipv6 router isis 1
+ !
+ interface eth-sw1
+ ipv6 address 4000:121::41/64
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0041.00
+ is-type level-1
+ topology ipv6-unicast
+ ppr on
+ !
+
+ switches:
+ sw1:
+ links:
+ eth-rt32:
+ peer: [rt32, eth-sw1]
+ eth-rt33:
+ peer: [rt33, eth-sw1]
+ eth-rt41:
+ peer: [rt41, eth-sw1]
+
+ frr:
+ base-config: |
+ hostname %(node)
+ password 1
+ log file %(logdir)/%(node).log
+ log commands
+ !
+ debug zebra rib
+ debug isis ppr
+ debug isis events
+ debug isis route-events
+ debug isis spf-events
+ debug isis lsp-gen
+ !
+
+YANG
+^^^^
+
+PPR can also be configured using NETCONF, RESTCONF and gRPC based on the
+following YANG models: \*
+`frr-ppr.yang <https://github.com/opensourcerouting/frr/blob/isisd-ppr/yang/frr-ppr.yang>`__
+\*
+`frr-isisd.yang <https://github.com/opensourcerouting/frr/blob/isisd-ppr/yang/frr-isisd.yang>`__
+
+As an example, here’s R11 configuration in the XML format:
+
+.. code:: xml
+
+ <lib xmlns="http://frrouting.org/yang/interface">
+ <interface>
+ <name>lo-ppr</name>
+ <vrf>default</vrf>
+ </interface>
+ <interface>
+ <name>lo</name>
+ <vrf>default</vrf>
+ <isis xmlns="http://frrouting.org/yang/isisd">
+ <area-tag>1</area-tag>
+ <ipv6-routing>true</ipv6-routing>
+ </isis>
+ </interface>
+ <interface>
+ <name>eth-ce1</name>
+ <vrf>default</vrf>
+ </interface>
+ <interface>
+ <name>eth-rt12</name>
+ <vrf>default</vrf>
+ <isis xmlns="http://frrouting.org/yang/isisd">
+ <area-tag>1</area-tag>
+ <ipv6-routing>true</ipv6-routing>
+ <hello>
+ <multiplier>
+ <level-1>3</level-1>
+ <level-2>3</level-2>
+ </multiplier>
+ </hello>
+ <network-type>point-to-point</network-type>
+ </isis>
+ </interface>
+ <interface>
+ <name>eth-rt21</name>
+ <vrf>default</vrf>
+ <isis xmlns="http://frrouting.org/yang/isisd">
+ <area-tag>1</area-tag>
+ <ipv6-routing>true</ipv6-routing>
+ <hello>
+ <multiplier>
+ <level-1>3</level-1>
+ <level-2>3</level-2>
+ </multiplier>
+ </hello>
+ <network-type>point-to-point</network-type>
+ </isis>
+ </interface>
+ </lib>
+ <ppr xmlns="http://frrouting.org/yang/ppr">
+ <group>
+ <name>VOIP</name>
+ <ipv6>
+ <ppr-id>6000:1::1/128</ppr-id>
+ <ppr-prefix>5000::11/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>5000::14/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::23/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::22/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::21/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::11/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <attributes>
+ <ppr-metric>50</ppr-metric>
+ </attributes>
+ </ipv6>
+ <ipv6>
+ <ppr-id>6000:2::1/128</ppr-id>
+ <ppr-prefix>5000::14/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>5000::11/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::21/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::22/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::23/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::14/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <attributes>
+ <ppr-metric>50</ppr-metric>
+ </attributes>
+ </ipv6>
+ </group>
+ <group>
+ <name>INTERFACE_PDES</name>
+ <ipv6>
+ <ppr-id>6000:1::2/128</ppr-id>
+ <ppr-prefix>5000::11/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>5000::14/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::23/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::33/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>4000:121::41/64</pde-id>
+ <pde-id-type>ipv6-interface</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::32/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>4000:113::21/64</pde-id>
+ <pde-id-type>ipv6-interface</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::11/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ </ipv6>
+ <ipv6>
+ <ppr-id>6000:2::2/128</ppr-id>
+ <ppr-prefix>5000::14/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>5000::11/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::21/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::32/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>4000:121::41/64</pde-id>
+ <pde-id-type>ipv6-interface</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::33/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>4000:116::23/64</pde-id>
+ <pde-id-type>ipv6-interface</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::14/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ </ipv6>
+ </group>
+ <group>
+ <name>BROKEN</name>
+ <ipv6>
+ <ppr-id>6000:1::3/128</ppr-id>
+ <ppr-prefix>5000::11/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>5000::14/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::23/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::99/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::21/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::11/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <attributes>
+ <ppr-metric>1500</ppr-metric>
+ </attributes>
+ </ipv6>
+ <ipv6>
+ <ppr-id>6000:2::3/128</ppr-id>
+ <ppr-prefix>5000::14/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>5000::11/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::21/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::99/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::23/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::14/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <attributes>
+ <ppr-metric>1500</ppr-metric>
+ </attributes>
+ </ipv6>
+ </group>
+ </ppr>
+ <isis xmlns="http://frrouting.org/yang/isisd">
+ <instance>
+ <area-tag>1</area-tag>
+ <area-address>49.0000.0000.0000.0011.00</area-address>
+ <multi-topology>
+ <ipv6-unicast>
+ </ipv6-unicast>
+ </multi-topology>
+ <ppr>
+ <enable>true</enable>
+ <ppr-advertise>
+ <name>VOIP</name>
+ </ppr-advertise>
+ <ppr-advertise>
+ <name>INTERFACE_PDES</name>
+ </ppr-advertise>
+ <ppr-advertise>
+ <name>BROKEN</name>
+ </ppr-advertise>
+ </ppr>
+ </instance>
+ </isis>
+
+Verification - Control Plane
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Verify that R11 has flooded the PPR TLVs correctly to all IS-IS routers:
+
+::
+
+ # show isis database detail 0000.0000.0011
+ Area 1:
+ IS-IS Level-1 link-state database:
+ LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL
+ debian.00-00 1233 0x00000009 0x7bd4 683 0/0/0
+ Protocols Supported: IPv4, IPv6
+ Area Address: 49.0000
+ MT Router Info: ipv4-unicast
+ MT Router Info: ipv6-unicast
+ Hostname: debian
+ MT Reachability: 0000.0000.0012.00 (Metric: 10) ipv6-unicast
+ MT Reachability: 0000.0000.0021.00 (Metric: 10) ipv6-unicast
+ MT IPv6 Reachability: 5000::11/128 (Metric: 10) ipv6-unicast
+ MT IPv6 Reachability: 4000:101::/64 (Metric: 10) ipv6-unicast
+ MT IPv6 Reachability: 4000:104::/64 (Metric: 10) ipv6-unicast
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::11/128
+ ID: 6000:1::3/128 (Native IPv6)
+ PDE: 5000::14/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::23/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::99/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::21/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::11/128 (IPv6 Node Address), L:0 N:1 E:0
+ Metric: 1500
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::14/128
+ ID: 6000:2::3/128 (Native IPv6)
+ PDE: 5000::11/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::21/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::99/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::23/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::14/128 (IPv6 Node Address), L:0 N:1 E:0
+ Metric: 1500
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::11/128
+ ID: 6000:1::2/128 (Native IPv6)
+ PDE: 5000::14/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::23/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::33/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 4000:121::41 (IPv6 Interface Address), L:0 N:0 E:0
+ PDE: 5000::32/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 4000:113::21 (IPv6 Interface Address), L:0 N:0 E:0
+ PDE: 5000::11/128 (IPv6 Node Address), L:0 N:1 E:0
+ Metric: 0
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::14/128
+ ID: 6000:2::2/128 (Native IPv6)
+ PDE: 5000::11/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::21/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::32/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 4000:121::41 (IPv6 Interface Address), L:0 N:0 E:0
+ PDE: 5000::33/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 4000:116::23 (IPv6 Interface Address), L:0 N:0 E:0
+ PDE: 5000::14/128 (IPv6 Node Address), L:0 N:1 E:0
+ Metric: 0
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::11/128
+ ID: 6000:1::1/128 (Native IPv6)
+ PDE: 5000::14/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::23/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::22/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::21/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::11/128 (IPv6 Node Address), L:0 N:1 E:0
+ Metric: 50
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::14/128
+ ID: 6000:2::1/128 (Native IPv6)
+ PDE: 5000::11/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::21/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::22/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::23/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::14/128 (IPv6 Node Address), L:0 N:1 E:0
+ Metric: 50
+
+The PPR TLVs can also be seen using a modified version of Wireshark as
+seen below:
+
+.. figure:: https://user-images.githubusercontent.com/931662/61582441-9551e500-ab01-11e9-8f6f-400ee3fba927.png
+ :alt: s2
+
+ s2
+
+Using the ``show isis ppr`` command, verify that all routers installed
+the PPR-IDs for the paths they are part of. Example:
+
+Router RT11
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ --------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Tail-End - -
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Tail-End - -
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Tail-End - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Head-End Up 00:45:41
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Head-End Up 00:45:41
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Head-End Up 00:45:41
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:2::1/128 [115/50] via fe80::c2a:54ff:fe39:bff7, eth-rt21, 00:01:33
+ I>* 6000:2::2/128 [115/0] via fe80::c2a:54ff:fe39:bff7, eth-rt21, 00:01:33
+ I>* 6000:2::3/128 [115/1500] via fe80::c2a:54ff:fe39:bff7, eth-rt21, 00:01:33
+
+Router RT12
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Off-Path - -
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Off-Path - -
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Off-Path - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT13
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Off-Path - -
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Off-Path - -
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Off-Path - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT14
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ --------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Head-End Up 00:45:45
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Head-End Up 00:45:45
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Head-End Up 00:45:45
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Tail-End - -
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Tail-End - -
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Tail-End - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::1/128 [115/50] via fe80::58ea:78ff:fe00:92c1, eth-rt23, 00:01:36
+ I>* 6000:1::2/128 [115/0] via fe80::58ea:78ff:fe00:92c1, eth-rt23, 00:01:36
+ I>* 6000:1::3/128 [115/1500] via fe80::58ea:78ff:fe00:92c1, eth-rt23, 00:01:36
+
+Router RT21
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Mid-Point Up 00:45:46
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Mid-Point Up 00:45:46
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Mid-Point Up 00:45:46
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Mid-Point Up 00:45:46
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Mid-Point Up 00:45:46
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Mid-Point Down -
+
+ # show isis ppr id ipv6 6000:2::3/128 detail
+ Area 1:
+ PPR-ID: 6000:2::3/128 (Native IPv6)
+ PPR-Prefix: 5000::14/128
+ PDEs:
+ 5000::11/128 (IPv6 Node Address)
+ 5000::21/128 (IPv6 Node Address) [LOCAL]
+ 5000::99/128 (IPv6 Node Address) [NEXT]
+ 5000::23/128 (IPv6 Node Address)
+ 5000::14/128 (IPv6 Node Address)
+ Attributes:
+ Metric: 1500
+ Position: Mid-Point
+ Originator: 0000.0000.0011
+ Level: L1
+ Algorithm: 1
+ MT-ID: ipv4-unicast
+ Status: Down: PDE is unreachable
+ Last change: 00:00:37
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::1/128 [115/50] via fe80::142e:79ff:feeb:cffc, eth-rt11, 00:01:38
+ I>* 6000:1::2/128 [115/0] via fe80::142e:79ff:feeb:cffc, eth-rt11, 00:01:38
+ I>* 6000:1::3/128 [115/1500] via fe80::142e:79ff:feeb:cffc, eth-rt11, 00:01:38
+ I>* 6000:2::1/128 [115/50] via fe80::c88e:7fff:fe5f:a08d, eth-rt22, 00:01:38
+ I>* 6000:2::2/128 [115/0] via fe80::8b2:9eff:fe98:f66a, eth-rt32, 00:01:38
+
+Router RT22
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Mid-Point Up 00:45:47
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Off-Path - -
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Mid-Point Up 00:45:47
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Off-Path - -
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Off-Path - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::1/128 [115/50] via fe80::2cb5:edff:fe60:29b1, eth-rt21, 00:01:38
+ I>* 6000:2::1/128 [115/50] via fe80::e8d9:63ff:fea3:177b, eth-rt23, 00:01:38
+
+Router RT23
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Mid-Point Up 00:45:49
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Mid-Point Up 00:45:49
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Mid-Point Down -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Mid-Point Up 00:45:49
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Mid-Point Up 00:45:49
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Mid-Point Up 00:45:49
+
+ # show isis ppr id ipv6 6000:1::3/128 detail
+ Area 1:
+ PPR-ID: 6000:1::3/128 (Native IPv6)
+ PPR-Prefix: 5000::11/128
+ PDEs:
+ 5000::14/128 (IPv6 Node Address)
+ 5000::23/128 (IPv6 Node Address) [LOCAL]
+ 5000::99/128 (IPv6 Node Address) [NEXT]
+ 5000::21/128 (IPv6 Node Address)
+ 5000::11/128 (IPv6 Node Address)
+ Attributes:
+ Metric: 1500
+ Position: Mid-Point
+ Originator: 0000.0000.0011
+ Level: L1
+ Algorithm: 1
+ MT-ID: ipv4-unicast
+ Status: Down: PDE is unreachable
+ Last change: 00:02:50
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::1/128 [115/50] via fe80::d09f:1bff:fe31:e9c9, eth-rt22, 00:01:40
+ I>* 6000:1::2/128 [115/0] via fe80::c0c3:b3ff:fe9f:b5d3, eth-rt33, 00:01:40
+ I>* 6000:2::1/128 [115/50] via fe80::f40a:66ff:fefc:5c32, eth-rt14, 00:01:40
+ I>* 6000:2::2/128 [115/0] via fe80::f40a:66ff:fefc:5c32, eth-rt14, 00:01:40
+ I>* 6000:2::3/128 [115/1500] via fe80::f40a:66ff:fefc:5c32, eth-rt14, 00:01:40
+
+Router RT31
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Off-Path - -
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Off-Path - -
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Off-Path - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT32
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Mid-Point Up 00:45:51
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Mid-Point Up 00:45:51
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Off-Path - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::2/128 [115/0] via 4000:113::21, eth-rt21, 00:01:42
+ I>* 6000:2::2/128 [115/0] via 4000:121::41, eth-sw1, 00:01:42
+
+Router RT33
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Mid-Point Up 00:45:52
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Mid-Point Up 00:45:52
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Off-Path - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::2/128 [115/0] via 4000:121::41, eth-sw1, 00:01:43
+ I>* 6000:2::2/128 [115/0] via 4000:116::23, eth-rt23, 00:01:43
+
+Router RT34
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Off-Path - -
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Off-Path - -
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Off-Path - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT41
+'''''''''''
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:1::2/128 (Native IPv6) 5000::11/128 0 Mid-Point Up 00:45:55
+ 1 L1 6000:1::3/128 (Native IPv6) 5000::11/128 1500 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+ 1 L1 6000:2::2/128 (Native IPv6) 5000::14/128 0 Mid-Point Up 00:45:55
+ 1 L1 6000:2::3/128 (Native IPv6) 5000::14/128 1500 Off-Path - -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::2/128 [115/0] via fe80::b4b9:60ff:feee:3c73, eth-sw1, 00:01:46
+ I>* 6000:2::2/128 [115/0] via fe80::bc2a:d9ff:fe65:97f2, eth-sw1, 00:01:46
+
+As it can be seen by the output of ``show isis ppr id ipv6 ... detail``,
+routers R21 and R23 couldn’t install the third PPR path because of an
+unreachable PDE (configuration error).
+
+Verification - Forwarding Plane
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+On Router R11, use the ``traceroute`` tool to ensure that the PPR paths
+were installed correctly in the network:
+
+::
+
+ root@rt11:~# traceroute 6000:2::1
+ traceroute to 6000:2::1 (6000:2::1), 30 hops max, 80 byte packets
+ 1 4000:104::21 (4000:104::21) 0.612 ms 0.221 ms 0.241 ms
+ 2 4000:110::22 (4000:110::22) 0.257 ms 0.113 ms 0.105 ms
+ 3 4000:111::23 (4000:111::23) 0.257 ms 0.151 ms 0.098 ms
+ 4 6000:2::1 (6000:2::1) 0.346 ms 0.139 ms 0.100 ms
+ root@rt11:~#
+ root@rt11:~# traceroute 6000:2::2
+ traceroute to 6000:2::2 (6000:2::2), 30 hops max, 80 byte packets
+ 1 4000:104::21 (4000:104::21) 4.383 ms 4.148 ms 0.044 ms
+ 2 4000:113::32 (4000:113::32) 0.272 ms 0.065 ms 0.064 ms
+ 3 4000:121::41 (4000:121::41) 0.263 ms 0.101 ms 0.086 ms
+ 4 4000:115::33 (4000:115::33) 0.351 ms 4000:119::33 (4000:119::33) 0.249 ms 4000:115::33 (4000:115::33) 0.153 ms
+ 5 4000:111::23 (4000:111::23) 0.232 ms 0.293 ms 0.131 ms
+ 6 6000:2::2 (6000:2::2) 0.184 ms 0.212 ms 0.140 ms
+ root@rt11:~#
+ root@rt11:~# traceroute 6000:2::3
+ traceroute to 6000:2::3 (6000:2::3), 30 hops max, 80 byte packets
+ 1 4000:104::21 (4000:104::21) 1.537 ms !N 1.347 ms !N 1.075 ms !N
+
+The failure on the third traceroute is expected since the 6000:2::3
+PPR-ID is misconfigured.
+
+Now ping Host 3 from Host 1 and use tcpdump or wireshark to verify that
+the ICMP packets are being tunneled using GRE and following the {R11 -
+R21 - R22 - R23 - R14} path. Here’s a wireshark capture between R11 and
+R21:
+
+.. figure:: https://user-images.githubusercontent.com/931662/61582398-d4cc0180-ab00-11e9-83a8-d219f98010b9.png
+ :alt: s1
+
+ s1
+
+Using ``traceroute`` it’s also possible to see that the ICMP packets are
+being tunneled through the IS-IS network:
+
+::
+
+ root@host1:~# traceroute fd00:20:1::1 -s fd00:10:1::1
+ traceroute to fd00:20:1::1 (fd00:20:1::1), 30 hops max, 80 byte packets
+ 1 fd00:10:1::100 (fd00:10:1::100) 0.354 ms 0.092 ms 0.031 ms
+ 2 fd00:10::11 (fd00:10::11) 0.125 ms 0.022 ms 0.026 ms
+ 3 * * *
+ 4 * * *
+ 5 fd00:20:1::1 (fd00:20:1::1) 0.235 ms 0.106 ms 0.091 ms
diff --git a/doc/developer/northbound/ppr-mpls-basic-test-topology.rst b/doc/developer/northbound/ppr-mpls-basic-test-topology.rst
new file mode 100644
index 0000000000..aceec5fa2f
--- /dev/null
+++ b/doc/developer/northbound/ppr-mpls-basic-test-topology.rst
@@ -0,0 +1,1986 @@
+IS-IS PPR Basic MPLS
+====================
+
+.. contents:: Table of contents
+ :local:
+ :backlinks: entry
+ :depth: 2
+
+Software
+~~~~~~~~
+
+The FRR PPR implementation for IS-IS is available here:
+https://github.com/opensourcerouting/frr/tree/isisd-ppr-sr
+
+Topology
+~~~~~~~~
+
+In this topology we have an IS-IS network consisting of 12 routers. CE1
+and CE2 are the consumer edges, connected to R11 and R14, respectively.
+Three hosts are connected to the CEs using only static routes.
+
+Router R11 advertises 6 PPR TLVs: \* **IPv6 prefixes 6000:1::1/128 and
+6000:2::1/128:** {R11 - R21 - R22 - R23 - R14} (IPv6 Node Addresses). \*
+**MPLS SR Prefix-SIDs 500 and 501:** {R11 - R21 - R22 - R23 - R14} (SR
+Prefix-SIDs). \* **MPLS SR Prefix-SIDs 502 and 503:** {R11 - R21 - R31 -
+R32 - R41 - R33 - R34 - R23 - R14} (SR Prefix-SIDs)
+
+PBR rules are configured on R11 and R14 to route the traffic between
+Host 1 and Host 3 using the first PPR tunnel, whereas all other traffic
+between CE1 and CE2 uses the second PPR tunnel.
+
+Additional information: \* Addresses in the 4000::/16 range refer to
+interface addresses, where the last hextet corresponds to the node ID.
+\* Addresses in the 5000::/16 range refer to loopback addresses, where
+the last hextet corresponds to the node ID. \* Addresses in the
+6000::/16 range refer to PPR-ID addresses.
+
+::
+
+ +-------+ +-------+ +-------+
+ | | | | | |
+ | HOST1 | | HOST2 | | HOST3 |
+ | | | | | |
+ +---+---+ +---+---+ +---+---+
+ | | |
+ |fd00:10:1::/64 | |
+ +-----+ +------+ fd00:20:1::/64|
+ | |fd00:10:2::/64 |
+ | | |
+ +-+--+--+ +---+---+
+ | | | |
+ | CE1 | | CE2 |
+ | | | |
+ +---+---+ +---+---+
+ | |
+ | |
+ |fd00:10:0::/64 fd00:20:0::/64|
+ | |
+ | |
+ +---+---+ +-------+ +-------+ +---+---+
+ | |4000:101::/64| |4000:102::/64| |4000:103::/64| |
+ | R11 +-------------+ R12 +-------------+ R13 +-------------+ R14 |
+ | | | | | | | |
+ +---+---+ +--+-+--+ +--+-+--+ +---+---+
+ | | | | | |
+ |4000:104::/64 | |4000:106::/64 | |4000:108::/64 |
+ +---------+ +--------+ +--------+ +--------+ +--------+ +---------+
+ | |4000:105::/64 | |4000:107::/64 | |4000:109::/64
+ | | | | | |
+ +--+-+--+ +--+-+--+ +--+-+--+
+ | |4000:110::/64| |4000:111::/64| |
+ | R21 +-------------+ R22 +-------------+ R23 |
+ | | | | | |
+ +--+-+--+ +--+-+--+ +--+-+--+
+ | | | | | |
+ | |4000:113::/64 | |4000:115::/64 | |4000:117::/64
+ +---------+ +--------+ +--------+ +--------+ +--------+ +---------+
+ |4000:112::/64 | |4000:114::/64 | |4000:116::/64 |
+ | | | | | |
+ +---+---+ +--+-+--+ +--+-+--+ +---+---+
+ | |4000:118::/64| |4000:119::/64| |4000:120::/64| |
+ | R31 +-------------+ R32 +-------------+ R33 +-------------+ R34 |
+ | | | | | | | |
+ +-------+ +---+---+ +---+---+ +-------+
+ | |
+ |4000:121::/64 |
+ +----------+----------+
+ |
+ |
+ +---+---+
+ | |
+ | R41 |
+ | |
+ +-------+
+
+Configuration
+~~~~~~~~~~~~~
+
+PPR TLV processing needs to be enabled on all IS-IS routers using the
+``ppr on`` command. The advertisements of all PPR TLVs is done by router
+R11.
+
+CLI configuration
+^^^^^^^^^^^^^^^^^
+
+.. code:: yaml
+
+ ---
+
+ routers:
+
+ host1:
+ links:
+ eth-ce1:
+ peer: [ce1, eth-host1]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-ce1
+ ipv6 address fd00:10:1::1/64
+ !
+ ipv6 route ::/0 fd00:10:1::100
+
+ host2:
+ links:
+ eth-ce1:
+ peer: [ce1, eth-host2]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-ce1
+ ipv6 address fd00:10:2::1/64
+ !
+ ipv6 route ::/0 fd00:10:2::100
+
+ host3:
+ links:
+ eth-ce2:
+ peer: [ce2, eth-host3]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-ce2
+ ipv6 address fd00:20:1::1/64
+ !
+ ipv6 route ::/0 fd00:20:1::100
+
+ ce1:
+ links:
+ eth-host1:
+ peer: [host1, eth-ce1]
+ eth-host2:
+ peer: [host2, eth-ce1]
+ eth-rt11:
+ peer: [rt11, eth-ce1]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-host1
+ ipv6 address fd00:10:1::100/64
+ !
+ interface eth-host2
+ ipv6 address fd00:10:2::100/64
+ !
+ interface eth-rt11
+ ipv6 address fd00:10:0::100/64
+ !
+ ipv6 route ::/0 fd00:10:0::11 label 16501
+
+ ce2:
+ links:
+ eth-host3:
+ peer: [host3, eth-ce2]
+ eth-rt14:
+ peer: [rt14, eth-ce2]
+ frr:
+ zebra:
+ staticd:
+ config: |
+ interface eth-host3
+ ipv6 address fd00:20:1::100/64
+ !
+ interface eth-rt14
+ ipv6 address fd00:20:0::100/64
+ !
+ ipv6 route ::/0 fd00:20:0::14 label 16500
+
+ rt11:
+ links:
+ lo:
+ mpls: yes
+ lo-ppr:
+ eth-ce1:
+ peer: [ce1, eth-rt11]
+ mpls: yes
+ eth-rt12:
+ peer: [rt12, eth-rt11]
+ mpls: yes
+ eth-rt21:
+ peer: [rt21, eth-rt11]
+ mpls: yes
+ shell: |
+ # GRE tunnel for preferred packets (PPR)
+ ip -6 tunnel add tun-ppr mode ip6gre remote 6000:2::1 local 6000:1::1 ttl 64
+ ip link set dev tun-ppr up
+ # PBR rules
+ ip -6 rule add from fd00:10:1::/64 to fd00:20:1::/64 iif eth-ce1 lookup 10000
+ ip -6 route add default dev tun-ppr table 10000
+ frr:
+ zebra:
+ staticd:
+ isisd:
+ config: |
+ interface lo-ppr
+ ipv6 address 6000:1::1/128
+ !
+ interface lo
+ ip address 10.0.0.11/32
+ ipv6 address 5000::11/128
+ ipv6 router isis 1
+ !
+ interface eth-ce1
+ ipv6 address fd00:10:0::11/64
+ !
+ interface eth-rt12
+ ipv6 address 4000:101::11/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt21
+ ipv6 address 4000:104::11/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ ipv6 route fd00:10::/32 fd00:10:0::100
+ !
+ ppr group PPR_IPV6
+ ppr ipv6 6000:1::1/128 prefix 5000::11/128 metric 50
+ pde ipv6-node 5000::14/128
+ pde ipv6-node 5000::23/128
+ pde ipv6-node 5000::22/128
+ pde ipv6-node 5000::21/128
+ pde ipv6-node 5000::11/128
+ !
+ ppr ipv6 6000:2::1/128 prefix 5000::14/128 metric 50
+ pde ipv6-node 5000::11/128
+ pde ipv6-node 5000::21/128
+ pde ipv6-node 5000::22/128
+ pde ipv6-node 5000::23/128
+ pde ipv6-node 5000::14/128
+ !
+ !
+ ppr group PPR_MPLS_1
+ ppr mpls 500 prefix 5000::11/128
+ pde prefix-sid 14
+ pde prefix-sid 23
+ pde prefix-sid 22
+ pde prefix-sid 21
+ pde prefix-sid 11
+ !
+ ppr mpls 501 prefix 5000::14/128
+ pde prefix-sid 11
+ pde prefix-sid 21
+ pde prefix-sid 22
+ pde prefix-sid 23
+ pde prefix-sid 14
+ !
+ !
+ ppr group PPR_MPLS_2
+ ppr mpls 502 prefix 5000::11/128
+ pde prefix-sid 14
+ pde prefix-sid 23
+ pde prefix-sid 34
+ pde prefix-sid 33
+ pde prefix-sid 41
+ pde prefix-sid 32
+ pde prefix-sid 31
+ pde prefix-sid 21
+ pde prefix-sid 11
+ !
+ ppr mpls 503 prefix 5000::14/128
+ pde prefix-sid 11
+ pde prefix-sid 21
+ pde prefix-sid 31
+ pde prefix-sid 32
+ pde prefix-sid 41
+ pde prefix-sid 33
+ pde prefix-sid 34
+ pde prefix-sid 23
+ pde prefix-sid 14
+ !
+ !
+ router isis 1
+ net 49.0000.0000.0000.0011.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::11/128 index 11 no-php-flag
+ ppr on
+ ppr advertise PPR_IPV6
+ ppr advertise PPR_MPLS_1
+ ppr advertise PPR_MPLS_2
+ !
+
+ rt12:
+ links:
+ lo:
+ mpls: yes
+ eth-rt11:
+ peer: [rt11, eth-rt12]
+ mpls: yes
+ eth-rt13:
+ peer: [rt13, eth-rt12]
+ mpls: yes
+ eth-rt21:
+ peer: [rt21, eth-rt12]
+ mpls: yes
+ eth-rt22:
+ peer: [rt22, eth-rt12]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.12/32
+ ipv6 address 5000::12/128
+ ipv6 router isis 1
+ !
+ interface eth-rt11
+ ipv6 address 4000:101::12/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt13
+ ipv6 address 4000:102::12/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt21
+ ipv6 address 4000:105::12/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:106::12/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0012.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::12/128 index 12 no-php-flag
+ ppr on
+ !
+
+ rt13:
+ links:
+ lo:
+ mpls: yes
+ eth-rt12:
+ peer: [rt12, eth-rt13]
+ mpls: yes
+ eth-rt14:
+ peer: [rt14, eth-rt13]
+ mpls: yes
+ eth-rt22:
+ peer: [rt22, eth-rt13]
+ mpls: yes
+ eth-rt23:
+ peer: [rt23, eth-rt13]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.13/32
+ ipv6 address 5000::13/128
+ ipv6 router isis 1
+ !
+ interface eth-rt12
+ ipv6 address 4000:102::13/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt14
+ ipv6 address 4000:103::13/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:107::13/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt23
+ ipv6 address 4000:108::13/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0013.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::13/128 index 13 no-php-flag
+ ppr on
+ !
+
+ rt14:
+ links:
+ lo:
+ mpls: yes
+ lo-ppr:
+ eth-ce2:
+ peer: [ce2, eth-rt14]
+ mpls: yes
+ eth-rt13:
+ peer: [rt13, eth-rt14]
+ mpls: yes
+ eth-rt23:
+ peer: [rt23, eth-rt14]
+ mpls: yes
+ shell: |
+ # GRE tunnel for preferred packets (PPR)
+ ip -6 tunnel add tun-ppr mode ip6gre remote 6000:1::1 local 6000:2::1 ttl 64
+ ip link set dev tun-ppr up
+ # PBR rules
+ ip -6 rule add from fd00:20:1::/64 to fd00:10:1::/64 iif eth-ce2 lookup 10000
+ ip -6 route add default dev tun-ppr table 10000
+ frr:
+ zebra:
+ staticd:
+ isisd:
+ config: |
+ interface lo-ppr
+ ipv6 address 6000:2::1/128
+ !
+ interface lo
+ ip address 10.0.0.14/32
+ ipv6 address 5000::14/128
+ ipv6 router isis 1
+ !
+ interface eth-ce2
+ ipv6 address fd00:20:0::14/64
+ !
+ interface eth-rt13
+ ipv6 address 4000:103::14/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt23
+ ipv6 address 4000:109::14/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ ipv6 route fd00:20::/32 fd00:20:0::100
+ !
+ router isis 1
+ net 49.0000.0000.0000.0014.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::14/128 index 14 no-php-flag
+ ppr on
+ !
+
+ rt21:
+ links:
+ lo:
+ mpls: yes
+ eth-rt11:
+ peer: [rt11, eth-rt21]
+ mpls: yes
+ eth-rt12:
+ peer: [rt12, eth-rt21]
+ mpls: yes
+ eth-rt22:
+ peer: [rt22, eth-rt21]
+ mpls: yes
+ eth-rt31:
+ peer: [rt31, eth-rt21]
+ mpls: yes
+ eth-rt32:
+ peer: [rt32, eth-rt21]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.21/32
+ ipv6 address 5000::21/128
+ ipv6 router isis 1
+ !
+ interface eth-rt11
+ ipv6 address 4000:104::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt12
+ ipv6 address 4000:105::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:110::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt31
+ ipv6 address 4000:112::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt32
+ ipv6 address 4000:113::21/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0021.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::21/128 index 21 no-php-flag
+ ppr on
+ !
+
+ rt22:
+ links:
+ lo:
+ mpls: yes
+ eth-rt12:
+ peer: [rt12, eth-rt22]
+ mpls: yes
+ eth-rt13:
+ peer: [rt13, eth-rt22]
+ mpls: yes
+ eth-rt21:
+ peer: [rt21, eth-rt22]
+ mpls: yes
+ eth-rt23:
+ peer: [rt23, eth-rt22]
+ mpls: yes
+ eth-rt32:
+ peer: [rt32, eth-rt22]
+ mpls: yes
+ eth-rt33:
+ mpls: yes
+ peer: [rt33, eth-rt22]
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.22/32
+ ipv6 address 5000::22/128
+ ipv6 router isis 1
+ !
+ interface eth-rt12
+ ipv6 address 4000:106::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt13
+ ipv6 address 4000:107::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt21
+ ipv6 address 4000:110::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt23
+ ipv6 address 4000:111::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt32
+ ipv6 address 4000:114::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt33
+ ipv6 address 4000:115::22/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0022.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::22/128 index 22 no-php-flag
+ ppr on
+ !
+
+ rt23:
+ links:
+ lo:
+ mpls: yes
+ eth-rt13:
+ peer: [rt13, eth-rt23]
+ mpls: yes
+ eth-rt14:
+ peer: [rt14, eth-rt23]
+ mpls: yes
+ eth-rt22:
+ peer: [rt22, eth-rt23]
+ mpls: yes
+ eth-rt33:
+ peer: [rt33, eth-rt23]
+ mpls: yes
+ eth-rt34:
+ peer: [rt34, eth-rt23]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.23/32
+ ipv6 address 5000::23/128
+ ipv6 router isis 1
+ !
+ interface eth-rt13
+ ipv6 address 4000:108::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt14
+ ipv6 address 4000:109::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:111::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt33
+ ipv6 address 4000:116::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt34
+ ipv6 address 4000:117::23/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0023.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 20000 27999
+ segment-routing prefix 5000::23/128 index 23 no-php-flag
+ ppr on
+ !
+
+ rt31:
+ links:
+ lo:
+ mpls: yes
+ eth-rt21:
+ peer: [rt21, eth-rt31]
+ mpls: yes
+ eth-rt32:
+ peer: [rt32, eth-rt31]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.31/32
+ ipv6 address 5000::31/128
+ ipv6 router isis 1
+ !
+ interface eth-rt21
+ ipv6 address 4000:112::31/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt32
+ ipv6 address 4000:118::31/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0031.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::31/128 index 31 no-php-flag
+ ppr on
+ !
+
+ rt32:
+ links:
+ lo:
+ mpls: yes
+ eth-rt21:
+ peer: [rt21, eth-rt32]
+ mpls: yes
+ eth-rt22:
+ peer: [rt22, eth-rt32]
+ mpls: yes
+ eth-rt31:
+ peer: [rt31, eth-rt32]
+ mpls: yes
+ eth-rt33:
+ peer: [rt33, eth-rt32]
+ mpls: yes
+ eth-sw1:
+ peer: [sw1, eth-rt32]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.32/32
+ ipv6 address 5000::32/128
+ ipv6 router isis 1
+ !
+ interface eth-rt21
+ ipv6 address 4000:113::32/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt22
+ ipv6 address 4000:114::32/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt31
+ ipv6 address 4000:118::32/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt33
+ ipv6 address 4000:119::32/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-sw1
+ ipv6 address 4000:121::32/64
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0032.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::32/128 index 32 no-php-flag
+ ppr on
+ !
+
+ rt33:
+ links:
+ lo:
+ mpls: yes
+ eth-rt22:
+ peer: [rt22, eth-rt33]
+ mpls: yes
+ eth-rt23:
+ peer: [rt23, eth-rt33]
+ mpls: yes
+ eth-rt32:
+ peer: [rt32, eth-rt33]
+ mpls: yes
+ eth-rt34:
+ peer: [rt34, eth-rt33]
+ mpls: yes
+ eth-sw1:
+ peer: [sw1, eth-rt33]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.33/32
+ ipv6 address 5000::33/128
+ ipv6 router isis 1
+ !
+ interface eth-rt22
+ ipv6 address 4000:115::33/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt23
+ ipv6 address 4000:116::33/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt32
+ ipv6 address 4000:119::33/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt34
+ ipv6 address 4000:120::33/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-sw1
+ ipv6 address 4000:121::33/64
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0033.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::33/128 index 33 no-php-flag
+ ppr on
+ !
+
+ rt34:
+ links:
+ lo:
+ mpls: yes
+ eth-rt23:
+ peer: [rt23, eth-rt34]
+ mpls: yes
+ eth-rt33:
+ peer: [rt33, eth-rt34]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.34/32
+ ipv6 address 5000::34/128
+ ipv6 router isis 1
+ !
+ interface eth-rt23
+ ipv6 address 4000:117::34/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ interface eth-rt33
+ ipv6 address 4000:120::34/64
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0034.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::34/128 index 34 no-php-flag
+ ppr on
+ !
+
+ rt41:
+ links:
+ lo:
+ mpls: yes
+ eth-sw1:
+ peer: [sw1, eth-rt41]
+ mpls: yes
+ frr:
+ zebra:
+ isisd:
+ config: |
+ interface lo
+ ip address 10.0.0.41/32
+ ipv6 address 5000::41/128
+ ipv6 router isis 1
+ !
+ interface eth-sw1
+ ipv6 address 4000:121::41/64
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ !
+ router isis 1
+ net 49.0000.0000.0000.0041.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing prefix 5000::41/128 index 41 no-php-flag
+ ppr on
+ !
+
+ switches:
+ sw1:
+ links:
+ eth-rt32:
+ peer: [rt32, eth-sw1]
+ eth-rt33:
+ peer: [rt33, eth-sw1]
+ eth-rt41:
+ peer: [rt41, eth-sw1]
+
+ frr:
+ #valgrind: yes
+ base-config: |
+ hostname %(node)
+ password 1
+ log file %(logdir)/%(node).log
+ log commands
+ !
+ debug zebra rib
+ debug isis sr-events
+ debug isis ppr
+ debug isis events
+ debug isis route-events
+ debug isis spf-events
+ debug isis lsp-gen
+ !
+
+..
+
+ NOTE: it’s of fundamental importance to enable MPLS processing on the
+ loopback interfaces, otherwise the tail-end routers of the PPR-MPLS
+ tunnels will drop the labeled packets they receive.
+
+YANG
+^^^^
+
+PPR can also be configured using NETCONF, RESTCONF and gRPC based on the
+following YANG models: \*
+`frr-ppr.yang <https://github.com/opensourcerouting/frr/blob/isisd-ppr/yang/frr-ppr.yang>`__
+\*
+`frr-isisd.yang <https://github.com/opensourcerouting/frr/blob/isisd-ppr/yang/frr-isisd.yang>`__
+
+As an example, here’s R11 configuration in the XML format:
+
+.. code:: xml
+
+ <lib xmlns="http://frrouting.org/yang/interface">
+ <interface>
+ <name>lo-ppr</name>
+ <vrf>default</vrf>
+ </interface>
+ <interface>
+ <name>lo</name>
+ <vrf>default</vrf>
+ <isis xmlns="http://frrouting.org/yang/isisd">
+ <area-tag>1</area-tag>
+ <ipv6-routing>true</ipv6-routing>
+ </isis>
+ </interface>
+ <interface>
+ <name>eth-ce1</name>
+ <vrf>default</vrf>
+ </interface>
+ <interface>
+ <name>eth-rt12</name>
+ <vrf>default</vrf>
+ <isis xmlns="http://frrouting.org/yang/isisd">
+ <area-tag>1</area-tag>
+ <ipv6-routing>true</ipv6-routing>
+ <hello>
+ <multiplier>
+ <level-1>3</level-1>
+ <level-2>3</level-2>
+ </multiplier>
+ </hello>
+ <network-type>point-to-point</network-type>
+ </isis>
+ </interface>
+ <interface>
+ <name>eth-rt21</name>
+ <vrf>default</vrf>
+ <isis xmlns="http://frrouting.org/yang/isisd">
+ <area-tag>1</area-tag>
+ <ipv6-routing>true</ipv6-routing>
+ <hello>
+ <multiplier>
+ <level-1>3</level-1>
+ <level-2>3</level-2>
+ </multiplier>
+ </hello>
+ <network-type>point-to-point</network-type>
+ </isis>
+ </interface>
+ </lib>
+ <ppr xmlns="http://frrouting.org/yang/ppr">
+ <group>
+ <name>PPR_IPV6</name>
+ <ipv6>
+ <ppr-id>6000:1::1/128</ppr-id>
+ <ppr-prefix>5000::11/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>5000::14/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::23/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::22/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::21/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::11/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <attributes>
+ <ppr-metric>50</ppr-metric>
+ </attributes>
+ </ipv6>
+ <ipv6>
+ <ppr-id>6000:2::1/128</ppr-id>
+ <ppr-prefix>5000::14/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>5000::11/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::21/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::22/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::23/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>5000::14/128</pde-id>
+ <pde-id-type>ipv6-node</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <attributes>
+ <ppr-metric>50</ppr-metric>
+ </attributes>
+ </ipv6>
+ </group>
+ <group>
+ <name>PPR_MPLS_1</name>
+ <mpls>
+ <ppr-id>500</ppr-id>
+ <ppr-prefix>5000::11/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>14</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>23</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>22</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>21</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>11</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ </mpls>
+ <mpls>
+ <ppr-id>501</ppr-id>
+ <ppr-prefix>5000::14/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>11</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>21</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>22</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>23</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>14</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ </mpls>
+ </group>
+ <group>
+ <name>PPR_MPLS_2</name>
+ <mpls>
+ <ppr-id>502</ppr-id>
+ <ppr-prefix>5000::11/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>14</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>23</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>34</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>33</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>41</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>32</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>31</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>21</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>11</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ </mpls>
+ <mpls>
+ <ppr-id>503</ppr-id>
+ <ppr-prefix>5000::14/128</ppr-prefix>
+ <ppr-pde>
+ <pde-id>11</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>21</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>31</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>32</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>41</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>33</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>34</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>23</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ <ppr-pde>
+ <pde-id>14</pde-id>
+ <pde-id-type>prefix-sid</pde-id-type>
+ <pde-type>topological</pde-type>
+ </ppr-pde>
+ </mpls>
+ </group>
+ </ppr>
+ <isis xmlns="http://frrouting.org/yang/isisd">
+ <instance>
+ <area-tag>1</area-tag>
+ <area-address>49.0000.0000.0000.0011.00</area-address>
+ <multi-topology>
+ <ipv6-unicast>
+ </ipv6-unicast>
+ </multi-topology>
+ <segment-routing>
+ <enabled>true</enabled>
+ <prefix-sid-map>
+ <prefix-sid>
+ <prefix>5000::11/128</prefix>
+ <sid-value>11</sid-value>
+ <last-hop-behavior>no-php</last-hop-behavior>
+ </prefix-sid>
+ </prefix-sid-map>
+ </segment-routing>
+ <ppr>
+ <enable>true</enable>
+ <ppr-advertise>
+ <name>PPR_IPV6</name>
+ </ppr-advertise>
+ <ppr-advertise>
+ <name>PPR_MPLS_1</name>
+ </ppr-advertise>
+ <ppr-advertise>
+ <name>PPR_MPLS_2</name>
+ </ppr-advertise>
+ </ppr>
+ </instance>
+ </isis>
+
+Verification - Control Plane
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Verify that R11 has flooded the PPR TLVs correctly to all IS-IS routers:
+
+::
+
+ # show isis database detail 0000.0000.0011
+ Area 1:
+ IS-IS Level-1 link-state database:
+ LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL
+ debian.00-00 * 980 0x00000003 0x3b69 894 0/0/0
+ Protocols Supported: IPv4, IPv6
+ Area Address: 49.0000
+ MT Router Info: ipv4-unicast
+ MT Router Info: ipv6-unicast
+ Hostname: debian
+ TE Router ID: 10.0.0.11
+ Router Capability: 10.0.0.11 , D:0, S:0
+ Segment Routing: I:1 V:1, SRGB Base: 16000 Range: 8000
+ Algorithm: 0: SPF 0: Strict SPF
+ MT Reachability: 0000.0000.0012.00 (Metric: 10) ipv6-unicast
+ Adjacency-SID: 16, Weight: 0, Flags: F:1 B:0, V:1, L:1, S:0, P:0
+ MT Reachability: 0000.0000.0021.00 (Metric: 10) ipv6-unicast
+ Adjacency-SID: 17, Weight: 0, Flags: F:1 B:0, V:1, L:1, S:0, P:0
+ IPv4 Interface Address: 10.0.0.11
+ Extended IP Reachability: 10.0.0.11/32 (Metric: 10)
+ MT IPv6 Reachability: 5000::11/128 (Metric: 10) ipv6-unicast
+ Subtlvs:
+ SR Prefix-SID Index: 11, Algorithm: 0, Flags: NO-PHP
+ MT IPv6 Reachability: 4000:101::/64 (Metric: 10) ipv6-unicast
+ MT IPv6 Reachability: 4000:104::/64 (Metric: 10) ipv6-unicast
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::11/128
+ ID: 6000:1::1/128 (Native IPv6)
+ PDE: 5000::14/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::23/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::22/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::21/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::11/128 (IPv6 Node Address), L:0 N:1 E:0
+ Metric: 50
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::14/128
+ ID: 6000:2::1/128 (Native IPv6)
+ PDE: 5000::11/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::21/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::22/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::23/128 (IPv6 Node Address), L:0 N:0 E:0
+ PDE: 5000::14/128 (IPv6 Node Address), L:0 N:1 E:0
+ Metric: 50
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::11/128
+ ID: 500 (MPLS)
+ PDE: 14 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 23 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 22 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 21 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 11 (SR-MPLS Prefix SID), L:0 N:1 E:0
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::14/128
+ ID: 501 (MPLS)
+ PDE: 11 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 21 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 22 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 23 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 14 (SR-MPLS Prefix SID), L:0 N:1 E:0
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::11/128
+ ID: 502 (MPLS)
+ PDE: 14 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 23 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 34 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 33 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 41 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 32 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 31 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 21 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 11 (SR-MPLS Prefix SID), L:0 N:1 E:0
+ PPR: Fragment ID: 0, MT-ID: ipv4-unicast, Algorithm: SPF, F:0 D:0 A:0 U:1
+ PPR Prefix: 5000::14/128
+ ID: 503 (MPLS)
+ PDE: 11 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 21 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 31 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 32 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 41 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 33 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 34 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 23 (SR-MPLS Prefix SID), L:0 N:0 E:0
+ PDE: 14 (SR-MPLS Prefix SID), L:0 N:1 E:0
+
+Using the ``show isis ppr`` command, verify that all routers installed
+the PPR-IDs for the paths they are part of. Example:
+
+Router RT11
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ --------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Tail-End Up 00:00:42
+ 1 L1 501 (MPLS) 5000::14/128 0 Head-End Up 00:00:41
+ 1 L1 502 (MPLS) 5000::11/128 0 Tail-End Up 00:00:42
+ 1 L1 503 (MPLS) 5000::14/128 0 Head-End Up 00:00:41
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Tail-End - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Head-End Up 00:00:41
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::2065:5ff:fe72:d6c5 implicit-null
+ 17 SR (IS-IS) fe80::345f:dfff:fea4:913d implicit-null
+ 16011 SR (IS-IS) lo -
+ 16012 SR (IS-IS) fe80::2065:5ff:fe72:d6c5 16012
+ 16013 SR (IS-IS) fe80::2065:5ff:fe72:d6c5 16013
+ 16014 SR (IS-IS) fe80::2065:5ff:fe72:d6c5 16014
+ 16021 SR (IS-IS) fe80::345f:dfff:fea4:913d 16021
+ 16022 SR (IS-IS) fe80::345f:dfff:fea4:913d 16022
+ 16022 SR (IS-IS) fe80::2065:5ff:fe72:d6c5 16022
+ 16023 SR (IS-IS) fe80::345f:dfff:fea4:913d 16023
+ 16023 SR (IS-IS) fe80::2065:5ff:fe72:d6c5 16023
+ 16031 SR (IS-IS) fe80::345f:dfff:fea4:913d 16031
+ 16032 SR (IS-IS) fe80::345f:dfff:fea4:913d 16032
+ 16033 SR (IS-IS) fe80::345f:dfff:fea4:913d 16033
+ 16033 SR (IS-IS) fe80::2065:5ff:fe72:d6c5 16033
+ 16034 SR (IS-IS) fe80::345f:dfff:fea4:913d 16034
+ 16034 SR (IS-IS) fe80::2065:5ff:fe72:d6c5 16034
+ 16041 SR (IS-IS) fe80::345f:dfff:fea4:913d 16041
+ 16500 PPR (IS-IS) lo -
+ 16501 PPR (IS-IS) fe80::345f:dfff:fea4:913d 16501
+ 16502 PPR (IS-IS) lo -
+ 16503 PPR (IS-IS) fe80::345f:dfff:fea4:913d 16503
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:2::1/128 [115/50] via fe80::345f:dfff:fea4:913d, eth-rt21, 00:00:41
+
+Router RT12
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 501 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 502 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 503 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ ----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::60ad:96ff:fe3f:9989 implicit-null
+ 17 SR (IS-IS) fe80::9cd2:25ff:febc:84c4 implicit-null
+ 18 SR (IS-IS) fe80::941c:12ff:fe55:8a12 implicit-null
+ 19 SR (IS-IS) fe80::78a7:59ff:fedc:48b8 implicit-null
+ 16011 SR (IS-IS) fe80::60ad:96ff:fe3f:9989 16011
+ 16012 SR (IS-IS) lo -
+ 16013 SR (IS-IS) fe80::9cd2:25ff:febc:84c4 16013
+ 16014 SR (IS-IS) fe80::9cd2:25ff:febc:84c4 16014
+ 16021 SR (IS-IS) fe80::941c:12ff:fe55:8a12 16021
+ 16022 SR (IS-IS) fe80::78a7:59ff:fedc:48b8 16022
+ 16023 SR (IS-IS) fe80::78a7:59ff:fedc:48b8 16023
+ 16023 SR (IS-IS) fe80::9cd2:25ff:febc:84c4 16023
+ 16031 SR (IS-IS) fe80::941c:12ff:fe55:8a12 16031
+ 16032 SR (IS-IS) fe80::78a7:59ff:fedc:48b8 16032
+ 16032 SR (IS-IS) fe80::941c:12ff:fe55:8a12 16032
+ 16033 SR (IS-IS) fe80::78a7:59ff:fedc:48b8 16033
+ 16034 SR (IS-IS) fe80::78a7:59ff:fedc:48b8 16034
+ 16034 SR (IS-IS) fe80::9cd2:25ff:febc:84c4 16034
+ 16041 SR (IS-IS) fe80::78a7:59ff:fedc:48b8 16041
+ 16041 SR (IS-IS) fe80::941c:12ff:fe55:8a12 16041
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT13
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 501 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 502 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 503 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ ----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::1c70:63ff:fe40:3a35 implicit-null
+ 17 SR (IS-IS) fe80::20:56ff:feff:b218 implicit-null
+ 18 SR (IS-IS) fe80::44c5:3fff:fe1e:f34a implicit-null
+ 19 SR (IS-IS) fe80::387d:34ff:fe02:87c3 implicit-null
+ 16011 SR (IS-IS) fe80::20:56ff:feff:b218 16011
+ 16012 SR (IS-IS) fe80::20:56ff:feff:b218 16012
+ 16013 SR (IS-IS) lo -
+ 16014 SR (IS-IS) fe80::1c70:63ff:fe40:3a35 16014
+ 16021 SR (IS-IS) fe80::387d:34ff:fe02:87c3 16021
+ 16021 SR (IS-IS) fe80::20:56ff:feff:b218 16021
+ 16022 SR (IS-IS) fe80::387d:34ff:fe02:87c3 16022
+ 16023 SR (IS-IS) fe80::44c5:3fff:fe1e:f34a 20023
+ 16031 SR (IS-IS) fe80::387d:34ff:fe02:87c3 16031
+ 16031 SR (IS-IS) fe80::20:56ff:feff:b218 16031
+ 16032 SR (IS-IS) fe80::387d:34ff:fe02:87c3 16032
+ 16033 SR (IS-IS) fe80::44c5:3fff:fe1e:f34a 20033
+ 16033 SR (IS-IS) fe80::387d:34ff:fe02:87c3 16033
+ 16034 SR (IS-IS) fe80::44c5:3fff:fe1e:f34a 20034
+ 16041 SR (IS-IS) fe80::44c5:3fff:fe1e:f34a 20041
+ 16041 SR (IS-IS) fe80::387d:34ff:fe02:87c3 16041
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT14
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ --------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Head-End Up 00:00:46
+ 1 L1 501 (MPLS) 5000::14/128 0 Tail-End Up 00:00:47
+ 1 L1 502 (MPLS) 5000::11/128 0 Head-End Up 00:00:46
+ 1 L1 503 (MPLS) 5000::14/128 0 Tail-End Up 00:00:47
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Head-End Up 00:00:46
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Tail-End - -
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::bcb5:99ff:fed7:22ad implicit-null
+ 17 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 implicit-null
+ 16011 SR (IS-IS) fe80::bcb5:99ff:fed7:22ad 16011
+ 16012 SR (IS-IS) fe80::bcb5:99ff:fed7:22ad 16012
+ 16013 SR (IS-IS) fe80::bcb5:99ff:fed7:22ad 16013
+ 16014 SR (IS-IS) lo -
+ 16021 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20021
+ 16021 SR (IS-IS) fe80::bcb5:99ff:fed7:22ad 16021
+ 16022 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20022
+ 16022 SR (IS-IS) fe80::bcb5:99ff:fed7:22ad 16022
+ 16023 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20023
+ 16031 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20031
+ 16031 SR (IS-IS) fe80::bcb5:99ff:fed7:22ad 16031
+ 16032 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20032
+ 16032 SR (IS-IS) fe80::bcb5:99ff:fed7:22ad 16032
+ 16033 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20033
+ 16034 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20034
+ 16041 SR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20041
+ 16500 PPR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20500
+ 16501 PPR (IS-IS) lo -
+ 16502 PPR (IS-IS) fe80::4c7b:a1ff:fe66:6ca7 20502
+ 16503 PPR (IS-IS) lo -
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::1/128 [115/50] via fe80::4c7b:a1ff:fe66:6ca7, eth-rt23, 00:00:02
+
+Router RT21
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:49
+ 1 L1 501 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:48
+ 1 L1 502 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:49
+ 1 L1 503 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:48
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Mid-Point Up 00:00:49
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Mid-Point Up 00:00:48
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::b886:2cff:fe84:a76f implicit-null
+ 17 SR (IS-IS) fe80::bc7e:bbff:fe7f:ecb0 implicit-null
+ 18 SR (IS-IS) fe80::e877:a2ff:feb7:4438 implicit-null
+ 19 SR (IS-IS) fe80::a0c2:82ff:fe39:204c implicit-null
+ 20 SR (IS-IS) fe80::ac6a:8aff:fe14:4f36 implicit-null
+ 16011 SR (IS-IS) fe80::e877:a2ff:feb7:4438 16011
+ 16012 SR (IS-IS) fe80::a0c2:82ff:fe39:204c 16012
+ 16013 SR (IS-IS) fe80::ac6a:8aff:fe14:4f36 16013
+ 16013 SR (IS-IS) fe80::a0c2:82ff:fe39:204c 16013
+ 16014 SR (IS-IS) fe80::ac6a:8aff:fe14:4f36 16014
+ 16014 SR (IS-IS) fe80::a0c2:82ff:fe39:204c 16014
+ 16021 SR (IS-IS) lo -
+ 16022 SR (IS-IS) fe80::ac6a:8aff:fe14:4f36 16022
+ 16023 SR (IS-IS) fe80::ac6a:8aff:fe14:4f36 16023
+ 16031 SR (IS-IS) fe80::bc7e:bbff:fe7f:ecb0 16031
+ 16032 SR (IS-IS) fe80::b886:2cff:fe84:a76f 16032
+ 16033 SR (IS-IS) fe80::b886:2cff:fe84:a76f 16033
+ 16033 SR (IS-IS) fe80::ac6a:8aff:fe14:4f36 16033
+ 16034 SR (IS-IS) fe80::b886:2cff:fe84:a76f 16034
+ 16034 SR (IS-IS) fe80::ac6a:8aff:fe14:4f36 16034
+ 16041 SR (IS-IS) fe80::b886:2cff:fe84:a76f 16041
+ 16500 PPR (IS-IS) fe80::e877:a2ff:feb7:4438 16500
+ 16501 PPR (IS-IS) fe80::ac6a:8aff:fe14:4f36 16501
+ 16502 PPR (IS-IS) fe80::e877:a2ff:feb7:4438 16502
+ 16503 PPR (IS-IS) fe80::bc7e:bbff:fe7f:ecb0 16503
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::1/128 [115/50] via fe80::e877:a2ff:feb7:4438, eth-rt11, 00:00:04
+ I>* 6000:2::1/128 [115/50] via fe80::ac6a:8aff:fe14:4f36, eth-rt22, 00:00:04
+
+Router RT22
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:50
+ 1 L1 501 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:50
+ 1 L1 502 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 503 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Mid-Point Up 00:00:50
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Mid-Point Up 00:00:50
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::3432:84ff:fe9d:2e41 implicit-null
+ 17 SR (IS-IS) fe80::c436:63ff:feb3:4f5d implicit-null
+ 18 SR (IS-IS) fe80::56:41ff:fe53:a6b2 implicit-null
+ 19 SR (IS-IS) fe80::b423:eaff:fea1:8247 implicit-null
+ 20 SR (IS-IS) fe80::9c2f:11ff:fe0a:ab34 implicit-null
+ 21 SR (IS-IS) fe80::7402:b8ff:fee9:682e implicit-null
+ 16011 SR (IS-IS) fe80::b423:eaff:fea1:8247 16011
+ 16011 SR (IS-IS) fe80::3432:84ff:fe9d:2e41 16011
+ 16012 SR (IS-IS) fe80::3432:84ff:fe9d:2e41 16012
+ 16013 SR (IS-IS) fe80::c436:63ff:feb3:4f5d 16013
+ 16014 SR (IS-IS) fe80::56:41ff:fe53:a6b2 20014
+ 16014 SR (IS-IS) fe80::c436:63ff:feb3:4f5d 16014
+ 16021 SR (IS-IS) fe80::b423:eaff:fea1:8247 16021
+ 16022 SR (IS-IS) lo -
+ 16023 SR (IS-IS) fe80::56:41ff:fe53:a6b2 20023
+ 16031 SR (IS-IS) fe80::9c2f:11ff:fe0a:ab34 16031
+ 16031 SR (IS-IS) fe80::b423:eaff:fea1:8247 16031
+ 16032 SR (IS-IS) fe80::9c2f:11ff:fe0a:ab34 16032
+ 16033 SR (IS-IS) fe80::7402:b8ff:fee9:682e 16033
+ 16034 SR (IS-IS) fe80::7402:b8ff:fee9:682e 16034
+ 16034 SR (IS-IS) fe80::56:41ff:fe53:a6b2 20034
+ 16041 SR (IS-IS) fe80::7402:b8ff:fee9:682e 16041
+ 16041 SR (IS-IS) fe80::9c2f:11ff:fe0a:ab34 16041
+ 16500 PPR (IS-IS) fe80::b423:eaff:fea1:8247 16500
+ 16501 PPR (IS-IS) fe80::56:41ff:fe53:a6b2 20501
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::1/128 [115/50] via fe80::b423:eaff:fea1:8247, eth-rt21, 00:00:06
+ I>* 6000:2::1/128 [115/50] via fe80::56:41ff:fe53:a6b2, eth-rt23, 00:00:06
+
+Router RT23
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:52
+ 1 L1 501 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:52
+ 1 L1 502 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:52
+ 1 L1 503 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:52
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Mid-Point Up 00:00:52
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Mid-Point Up 00:00:52
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::c4ca:41ff:fe2d:de8c implicit-null
+ 17 SR (IS-IS) fe80::a02b:1eff:fed6:97e4 implicit-null
+ 18 SR (IS-IS) fe80::5c15:8aff:feea:1d07 implicit-null
+ 19 SR (IS-IS) fe80::a42f:50ff:fe9c:af9f implicit-null
+ 20 SR (IS-IS) fe80::d0dc:6eff:fe71:9f19 implicit-null
+ 20011 SR (IS-IS) fe80::5c15:8aff:feea:1d07 16011
+ 20011 SR (IS-IS) fe80::a02b:1eff:fed6:97e4 16011
+ 20012 SR (IS-IS) fe80::5c15:8aff:feea:1d07 16012
+ 20012 SR (IS-IS) fe80::a02b:1eff:fed6:97e4 16012
+ 20013 SR (IS-IS) fe80::a02b:1eff:fed6:97e4 16013
+ 20014 SR (IS-IS) fe80::c4ca:41ff:fe2d:de8c 16014
+ 20021 SR (IS-IS) fe80::5c15:8aff:feea:1d07 16021
+ 20022 SR (IS-IS) fe80::5c15:8aff:feea:1d07 16022
+ 20023 SR (IS-IS) lo -
+ 20031 SR (IS-IS) fe80::a42f:50ff:fe9c:af9f 16031
+ 20031 SR (IS-IS) fe80::5c15:8aff:feea:1d07 16031
+ 20032 SR (IS-IS) fe80::a42f:50ff:fe9c:af9f 16032
+ 20032 SR (IS-IS) fe80::5c15:8aff:feea:1d07 16032
+ 20033 SR (IS-IS) fe80::a42f:50ff:fe9c:af9f 16033
+ 20034 SR (IS-IS) fe80::d0dc:6eff:fe71:9f19 16034
+ 20041 SR (IS-IS) fe80::a42f:50ff:fe9c:af9f 16041
+ 20500 PPR (IS-IS) fe80::5c15:8aff:feea:1d07 16500
+ 20501 PPR (IS-IS) fe80::c4ca:41ff:fe2d:de8c 16501
+ 20502 PPR (IS-IS) fe80::d0dc:6eff:fe71:9f19 16502
+ 20503 PPR (IS-IS) fe80::c4ca:41ff:fe2d:de8c 16503
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+ Codes: K - kernel route, C - connected, S - static, R - RIPng,
+ O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
+ v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
+ f - OpenFabric,
+ > - selected route, * - FIB route, q - queued route, r - rejected route
+
+ I>* 6000:1::1/128 [115/50] via fe80::5c15:8aff:feea:1d07, eth-rt22, 00:00:07
+ I>* 6000:2::1/128 [115/50] via fe80::c4ca:41ff:fe2d:de8c, eth-rt14, 00:00:07
+
+Router RT31
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 501 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 502 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:54
+ 1 L1 503 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:54
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::a067:c6ff:fe2c:3385 implicit-null
+ 17 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 implicit-null
+ 16011 SR (IS-IS) fe80::a067:c6ff:fe2c:3385 16011
+ 16012 SR (IS-IS) fe80::a067:c6ff:fe2c:3385 16012
+ 16013 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16013
+ 16013 SR (IS-IS) fe80::a067:c6ff:fe2c:3385 16013
+ 16014 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16014
+ 16014 SR (IS-IS) fe80::a067:c6ff:fe2c:3385 16014
+ 16021 SR (IS-IS) fe80::a067:c6ff:fe2c:3385 16021
+ 16022 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16022
+ 16022 SR (IS-IS) fe80::a067:c6ff:fe2c:3385 16022
+ 16023 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16023
+ 16023 SR (IS-IS) fe80::a067:c6ff:fe2c:3385 16023
+ 16031 SR (IS-IS) lo -
+ 16032 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16032
+ 16033 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16033
+ 16034 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16034
+ 16041 SR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16041
+ 16502 PPR (IS-IS) fe80::a067:c6ff:fe2c:3385 16502
+ 16503 PPR (IS-IS) fe80::f46d:c8ff:fe8a:a341 16503
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT32
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 501 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 502 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:55
+ 1 L1 503 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:55
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::881f:d3ff:febd:9e8c implicit-null
+ 17 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 implicit-null
+ 18 SR (IS-IS) fe80::9863:abff:fed0:d7e implicit-null
+ 19 SR (IS-IS) fe80::ec65:d1ff:fe32:b508 implicit-null
+ 20 SR (IS-IS) fe80::a4e9:77ff:feaa:f690 implicit-null
+ 21 SR (IS-IS) fe80::40c4:e6ff:fe26:767f implicit-null
+ 16011 SR (IS-IS) fe80::881f:d3ff:febd:9e8c 16011
+ 16012 SR (IS-IS) fe80::40c4:e6ff:fe26:767f 16012
+ 16012 SR (IS-IS) fe80::881f:d3ff:febd:9e8c 16012
+ 16013 SR (IS-IS) fe80::40c4:e6ff:fe26:767f 16013
+ 16014 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16014
+ 16014 SR (IS-IS) fe80::ec65:d1ff:fe32:b508 16014
+ 16014 SR (IS-IS) fe80::40c4:e6ff:fe26:767f 16014
+ 16021 SR (IS-IS) fe80::881f:d3ff:febd:9e8c 16021
+ 16022 SR (IS-IS) fe80::40c4:e6ff:fe26:767f 16022
+ 16023 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16023
+ 16023 SR (IS-IS) fe80::ec65:d1ff:fe32:b508 16023
+ 16023 SR (IS-IS) fe80::40c4:e6ff:fe26:767f 16023
+ 16031 SR (IS-IS) fe80::9863:abff:fed0:d7e 16031
+ 16032 SR (IS-IS) lo -
+ 16033 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16033
+ 16033 SR (IS-IS) fe80::ec65:d1ff:fe32:b508 16033
+ 16034 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16034
+ 16034 SR (IS-IS) fe80::ec65:d1ff:fe32:b508 16034
+ 16041 SR (IS-IS) fe80::a4e9:77ff:feaa:f690 16041
+ 16502 PPR (IS-IS) fe80::9863:abff:fed0:d7e 16502
+ 16503 PPR (IS-IS) fe80::a4e9:77ff:feaa:f690 16503
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT33
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 501 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 502 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:57
+ 1 L1 503 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:57
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::2832:a9ff:fec3:7078 implicit-null
+ 17 SR (IS-IS) fe80::7806:e1ff:fe72:9b1f implicit-null
+ 18 SR (IS-IS) fe80::5476:31ff:fe94:c39 implicit-null
+ 19 SR (IS-IS) fe80::a4e9:77ff:feaa:f690 implicit-null
+ 20 SR (IS-IS) fe80::68c9:2ff:fe04:5eba implicit-null
+ 21 SR (IS-IS) fe80::d053:97ff:fee2:1711 implicit-null
+ 16011 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16011
+ 16011 SR (IS-IS) fe80::5476:31ff:fe94:c39 16011
+ 16011 SR (IS-IS) fe80::d053:97ff:fee2:1711 16011
+ 16012 SR (IS-IS) fe80::d053:97ff:fee2:1711 16012
+ 16013 SR (IS-IS) fe80::68c9:2ff:fe04:5eba 20013
+ 16013 SR (IS-IS) fe80::d053:97ff:fee2:1711 16013
+ 16014 SR (IS-IS) fe80::68c9:2ff:fe04:5eba 20014
+ 16021 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16021
+ 16021 SR (IS-IS) fe80::5476:31ff:fe94:c39 16021
+ 16021 SR (IS-IS) fe80::d053:97ff:fee2:1711 16021
+ 16022 SR (IS-IS) fe80::d053:97ff:fee2:1711 16022
+ 16023 SR (IS-IS) fe80::68c9:2ff:fe04:5eba 20023
+ 16031 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16031
+ 16031 SR (IS-IS) fe80::5476:31ff:fe94:c39 16031
+ 16032 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16032
+ 16032 SR (IS-IS) fe80::5476:31ff:fe94:c39 16032
+ 16033 SR (IS-IS) lo -
+ 16034 SR (IS-IS) fe80::7806:e1ff:fe72:9b1f 16034
+ 16041 SR (IS-IS) fe80::a4e9:77ff:feaa:f690 16041
+ 16502 PPR (IS-IS) fe80::a4e9:77ff:feaa:f690 16502
+ 16503 PPR (IS-IS) fe80::7806:e1ff:fe72:9b1f 16503
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT34
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 501 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 502 (MPLS) 5000::11/128 0 Mid-Point Up 00:00:59
+ 1 L1 503 (MPLS) 5000::14/128 0 Mid-Point Up 00:00:59
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::ac33:5dff:fe99:81ec implicit-null
+ 17 SR (IS-IS) fe80::f009:b9ff:fe05:e540 implicit-null
+ 16011 SR (IS-IS) fe80::ac33:5dff:fe99:81ec 16011
+ 16011 SR (IS-IS) fe80::f009:b9ff:fe05:e540 20011
+ 16012 SR (IS-IS) fe80::ac33:5dff:fe99:81ec 16012
+ 16012 SR (IS-IS) fe80::f009:b9ff:fe05:e540 20012
+ 16013 SR (IS-IS) fe80::f009:b9ff:fe05:e540 20013
+ 16014 SR (IS-IS) fe80::f009:b9ff:fe05:e540 20014
+ 16021 SR (IS-IS) fe80::ac33:5dff:fe99:81ec 16021
+ 16021 SR (IS-IS) fe80::f009:b9ff:fe05:e540 20021
+ 16022 SR (IS-IS) fe80::ac33:5dff:fe99:81ec 16022
+ 16022 SR (IS-IS) fe80::f009:b9ff:fe05:e540 20022
+ 16023 SR (IS-IS) fe80::f009:b9ff:fe05:e540 20023
+ 16031 SR (IS-IS) fe80::ac33:5dff:fe99:81ec 16031
+ 16032 SR (IS-IS) fe80::ac33:5dff:fe99:81ec 16032
+ 16033 SR (IS-IS) fe80::ac33:5dff:fe99:81ec 16033
+ 16034 SR (IS-IS) lo -
+ 16041 SR (IS-IS) fe80::ac33:5dff:fe99:81ec 16041
+ 16502 PPR (IS-IS) fe80::ac33:5dff:fe99:81ec 16502
+ 16503 PPR (IS-IS) fe80::f009:b9ff:fe05:e540 20503
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Router RT41
+^^^^^^^^^^^
+
+::
+
+ # show isis ppr
+ Area Level ID Prefix Metric Position Status Uptime
+ ---------------------------------------------------------------------------------------------
+ 1 L1 500 (MPLS) 5000::11/128 0 Off-Path - -
+ 1 L1 501 (MPLS) 5000::14/128 0 Off-Path - -
+ 1 L1 502 (MPLS) 5000::11/128 0 Mid-Point Up 00:01:01
+ 1 L1 503 (MPLS) 5000::14/128 0 Mid-Point Up 00:01:01
+ 1 L1 6000:1::1/128 (Native IPv6) 5000::11/128 50 Off-Path - -
+ 1 L1 6000:2::1/128 (Native IPv6) 5000::14/128 50 Off-Path - -
+
+ # show mpls table
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------------------------------
+ 16 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 implicit-null
+ 17 SR (IS-IS) fe80::2832:a9ff:fec3:7078 implicit-null
+ 16011 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16011
+ 16012 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16012
+ 16012 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16012
+ 16013 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16013
+ 16013 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16013
+ 16014 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16014
+ 16021 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16021
+ 16022 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16022
+ 16022 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16022
+ 16023 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16023
+ 16031 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16031
+ 16032 SR (IS-IS) fe80::2832:a9ff:fec3:7078 16032
+ 16033 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16033
+ 16034 SR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16034
+ 16041 SR (IS-IS) lo -
+ 16502 PPR (IS-IS) fe80::2832:a9ff:fec3:7078 16502
+ 16503 PPR (IS-IS) fe80::1c7e:c3ff:fe5e:7a54 16503
+
+ # show ipv6 route 6000::/16 longer-prefixes isis
+
+Notice how R23 uses a different SRGB compared to the other routers in
+the network. As such, this router install different labels for PPR-IDs
+500 and 501 (e.g. 20500 instead of 16500 using the default SRGB).
+
+Verification - Forwarding Plane
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Ping Host 3 from Host2 and use tcpdump or wireshark to verify that the
+ICMP packets are being tunneled using MPLS LSPs and following the {R11 -
+R21 - R22 - R23 - R14} path. Here’s a wireshark capture between R11 and
+R21:
+
+.. figure:: https://user-images.githubusercontent.com/931662/64057179-2e980080-cb70-11e9-89c3-ff43e6d66cae.png
+ :alt: wireshark
+
+ wireshark
+
+Using ``traceroute`` it’s also possible to see that the ICMP packets are
+being tunneled through the IS-IS network:
+
+::
+
+ root@host2:~# traceroute -n fd00:20:1::1 -s fd00:10:2::1
+ traceroute to fd00:20:1::1 (fd00:20:1::1), 30 hops max, 80 byte packets
+ 1 fd00:10:2::100 1.996 ms 1.832 ms 1.725 ms
+ 2 * * *
+ 3 * * *
+ 4 * * *
+ 5 * * *
+ 6 * * *
+ 7 * * *
+ 8 fd00:20::100 0.154 ms 0.191 ms 0.116 ms
+ 9 fd00:20:1::1 0.125 ms 0.105 ms 0.104 ms
diff --git a/doc/developer/northbound/retrofitting-configuration-commands.rst b/doc/developer/northbound/retrofitting-configuration-commands.rst
new file mode 100644
index 0000000000..d328be9506
--- /dev/null
+++ b/doc/developer/northbound/retrofitting-configuration-commands.rst
@@ -0,0 +1,1928 @@
+
+.. _nb-retrofit:
+
+Retrofitting Configuration Commands
+===================================
+
+.. contents:: Table of contents
+ :local:
+ :backlinks: entry
+ :depth: 2
+
+Retrofitting process
+--------------------
+
+This page explains how to convert existing CLI configuration commands to
+the new northbound model. This documentation is meant to be the primary
+reference for developers working on the northbound retrofitting process.
+We’ll show several examples taken from the ripd northbound conversion to
+illustrate some concepts described herein.
+
+Step 1: writing a YANG module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first step is to write a YANG module that models faithfully the
+commands that are going to be converted. As explained in the
+[[Architecture]] page, the goal is to introduce the new YANG-based
+Northbound API without introducing backward incompatible changes in the
+CLI. The northbound retrofitting process should be completely
+transparent to FRR users.
+
+The developer is free to choose whether to write a full YANG module or a
+partial YANG module and increment it gradually. For developers who lack
+experience with YANG it’s probably a better idea to model one command at
+time.
+
+It’s recommended to reuse definitions from standard YANG models whenever
+possible to facilitate the process of writing module translators using
+the [[YANG module translator]]. As an example, the frr-ripd YANG module
+incorporated several parts of the IETF RIP YANG module. The repositories
+below contain big collections of YANG models that might be used as a
+reference:
+
+* https://github.com/YangModels/yang
+
+* https://github.com/openconfig/public
+
+When writing a YANG module, it’s highly recommended to follow the
+guidelines from `RFC 6087 <https://tools.ietf.org/html/rfc6087>`__. In
+general, most commands should be modeled fairly easy. Here are a few
+guidelines specific to authors of FRR YANG models:
+
+* Use presence-containers or lists to model commands that change the CLI node
+ (e.g. ``router rip``, ``interface eth0``). This way, if the presence-container
+ or list entry is removed, all configuration options below them are removed
+ automatically (exactly like the CLI behaves when a configuration object is
+ removed using a *no* command). This recommendation is orthogonal to the `YANG
+ authoring guidelines for OpenConfig models
+ <https://github.com/openconfig/public/blob/master/doc/openconfig_style_guide.md>`__
+ where the use of presence containers is discouraged. OpenConfig YANG models
+ however were not designed to replicate the behavior of legacy CLI commands.
+
+* When using YANG lists, be careful to identify what should be the key leaves.
+ In the ``offset-list WORD <in|out> (0-16) IFNAME`` command, for example, both
+ the direction (``<in|out>``) and the interface name should be the keys of the
+ list. This can be only known by analyzing the data structures used to store
+ the commands.
+
+* For clarity, use non-presence containers to group leaves that are associated
+ to the same configuration command (as we’ll see later, this also facilitate
+ the process of writing ``cli_show`` callbacks).
+
+* YANG leaves of type *enumeration* should define explicitly the value of each
+ *enum* option based on the value used in the FRR source code.
+
+* Default values should be taken from the source code whenever they exist.
+
+Some commands are more difficult to model and demand the use of more
+advanced YANG constructs like *choice*, *when* and *must* statements.
+**One key requirement is that it should be impossible to load an invalid
+JSON/XML configuration to FRR**. The YANG modules should model exactly
+what the CLI accepts in the form of commands, and all restrictions
+imposed by the CLI should be defined in the YANG models whenever
+possible. As we’ll see later, not all constraints can be expressed using
+the YANG language and sometimes we’ll need to resort to code-level
+validation in the northbound callbacks.
+
+ Tip: the :doc:`yang-tools` page details several tools and commands that
+ might be useful when writing a YANG module, like validating YANG
+ files, indenting YANG files, validating instance data, etc.
+
+In the example YANG snippet below, we can see the use of the *must*
+statement that prevents ripd from redistributing RIP routes into itself.
+Although ripd CLI doesn’t allow the operator to enter *redistribute rip*
+under *router rip*, we don’t have the same protection when configuring
+ripd using other northbound interfaces (e.g. NETCONF). So without this
+constraint it would be possible to feed an invalid configuration to ripd
+(i.e. a bug).
+
+.. code:: yang
+
+ list redistribute {
+ key "protocol";
+ description
+ "Redistributes routes learned from other routing protocols.";
+ leaf protocol {
+ type frr-route-types:frr-route-types-v4;
+ description
+ "Routing protocol.";
+ must '. != "rip"';
+ }
+ [snip]
+ }
+
+In the example below, we use the YANG *choice* statement to ensure that
+either the ``password`` leaf or the ``key-chain`` leaf is configured,
+but not both. This is in accordance to the sanity checks performed by
+the *ip rip authentication* commands.
+
+.. code:: yang
+
+ choice authentication-data {
+ description
+ "Choose whether to use a simple password or a key-chain.";
+ leaf authentication-password {
+ type string {
+ length "1..16";
+ }
+ description
+ "Authentication string.";
+ }
+ leaf authentication-key-chain {
+ type string;
+ description
+ "Key-chain name.";
+ }
+ }
+
+Once finished, the new YANG model should be put into the FRR *yang/* top
+level directory. This will ensure it will be installed automatically by
+``make install``. It’s also encouraged (but not required) to put sample
+configurations under *yang/examples/* using either JSON or XML files.
+
+Step 2: generate skeleton northbound callbacks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Use the *gen_northbound_callbacks* tool to generate skeleton callbacks
+for the YANG module. Example:
+
+.. code:: sh
+
+ $ tools/gen_northbound_callbacks frr-ripd > ripd/rip_northbound.c
+
+The tool will look for the given module in the ``YANG_MODELS_PATH``
+directory defined during the installation. For each schema node of the
+YANG module, the tool will generate skeleton callbacks based on the
+properties of the node. Example:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:ripd/instance
+ */
+ static int ripd_instance_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+ {
+ /* TODO: implement me. */
+ return NB_OK;
+ }
+
+ static int ripd_instance_delete(enum nb_event event,
+ const struct lyd_node *dnode)
+ {
+ /* TODO: implement me. */
+ return NB_OK;
+ }
+
+ /*
+ * XPath: /frr-ripd:ripd/instance/allow-ecmp
+ */
+ static int ripd_instance_allow_ecmp_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+ {
+ /* TODO: implement me. */
+ return NB_OK;
+ }
+
+ [snip]
+
+ const struct frr_yang_module_info frr_ripd_info = {
+ .name = "frr-ripd",
+ .nodes = {
+ {
+ .xpath = "/frr-ripd:ripd/instance",
+ .cbs.create = ripd_instance_create,
+ .cbs.delete = ripd_instance_delete,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/allow-ecmp",
+ .cbs.modify = ripd_instance_allow_ecmp_modify,
+ },
+ [snip]
+ {
+ .xpath = "/frr-ripd:ripd/state/routes/route",
+ .cbs.get_next = ripd_state_routes_route_get_next,
+ .cbs.get_keys = ripd_state_routes_route_get_keys,
+ .cbs.lookup_entry = ripd_state_routes_route_lookup_entry,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/state/routes/route/prefix",
+ .cbs.get_elem = ripd_state_routes_route_prefix_get_elem,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/state/routes/route/next-hop",
+ .cbs.get_elem = ripd_state_routes_route_next_hop_get_elem,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/state/routes/route/interface",
+ .cbs.get_elem = ripd_state_routes_route_interface_get_elem,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/state/routes/route/metric",
+ .cbs.get_elem = ripd_state_routes_route_metric_get_elem,
+ },
+ {
+ .xpath = "/frr-ripd:clear-rip-route",
+ .cbs.rpc = clear_rip_route_rpc,
+ },
+ [snip]
+
+After the C source file is generated, it’s necessary to add a copyright
+header on it and indent the code using ``clang-format``.
+
+Step 3: update the *frr_yang_module_info* array of all relevant daemons
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We must inform the northbound about which daemons will implement the new
+YANG module. This is done by updating the ``frr_daemon_info`` structure
+of these daemons, with help of the ``FRR_DAEMON_INFO`` macro.
+
+When a YANG module is specific to a single daemon, like the frr-ripd
+module, then only the corresponding daemon should be updated. When the
+YANG module is related to a subset of libfrr (e.g. route-maps), then all
+FRR daemons that make use of that subset must be updated.
+
+Example:
+
+.. code:: c
+
+ static const struct frr_yang_module_info *ripd_yang_modules[] = {
+ &frr_interface_info,
+ &frr_ripd_info,
+ };
+
+ FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT,
+ [snip]
+ .yang_modules = ripd_yang_modules,
+ .n_yang_modules = array_size(ripd_yang_modules), )
+
+Step 4: implement the northbound configuration callbacks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Implementing the northbound configuration callbacks consists mostly of
+copying code from the corresponding CLI commands and make the required
+adaptations.
+
+It’s recommended to convert one command or a small group of related
+commands per commit. Small commits are preferred to facilitate the
+review process. Both “old” and “new” command can coexist without
+problems, so the retrofitting process can happen gradually over time.
+
+The configuration callbacks
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+These are the four main northbound configuration callbacks, as defined
+in the ``lib/northbound.h`` file:
+
+.. code:: c
+
+ /*
+ * Configuration callback.
+ *
+ * A presence container, list entry, leaf-list entry or leaf of type
+ * empty has been created.
+ *
+ * For presence-containers and list entries, the callback is supposed to
+ * initialize the default values of its children (if any) from the YANG
+ * models.
+ *
+ * event
+ * The transaction phase. Refer to the documentation comments of
+ * nb_event for more details.
+ *
+ * dnode
+ * libyang data node that is being created.
+ *
+ * resource
+ * Pointer to store resource(s) allocated during the NB_EV_PREPARE
+ * phase. The same pointer can be used during the NB_EV_ABORT and
+ * NB_EV_APPLY phases to either release or make use of the allocated
+ * resource(s). It's set to NULL when the event is NB_EV_VALIDATE.
+ *
+ * Returns:
+ * - NB_OK on success.
+ * - NB_ERR_VALIDATION when a validation error occurred.
+ * - NB_ERR_RESOURCE when the callback failed to allocate a resource.
+ * - NB_ERR_INCONSISTENCY when an inconsistency was detected.
+ * - NB_ERR for other errors.
+ */
+ int (*create)(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+
+ /*
+ * Configuration callback.
+ *
+ * The value of a leaf has been modified.
+ *
+ * List keys don't need to implement this callback. When a list key is
+ * modified, the northbound treats this as if the list was deleted and a
+ * new one created with the updated key value.
+ *
+ * event
+ * The transaction phase. Refer to the documentation comments of
+ * nb_event for more details.
+ *
+ * dnode
+ * libyang data node that is being modified
+ *
+ * resource
+ * Pointer to store resource(s) allocated during the NB_EV_PREPARE
+ * phase. The same pointer can be used during the NB_EV_ABORT and
+ * NB_EV_APPLY phases to either release or make use of the allocated
+ * resource(s). It's set to NULL when the event is NB_EV_VALIDATE.
+ *
+ * Returns:
+ * - NB_OK on success.
+ * - NB_ERR_VALIDATION when a validation error occurred.
+ * - NB_ERR_RESOURCE when the callback failed to allocate a resource.
+ * - NB_ERR_INCONSISTENCY when an inconsistency was detected.
+ * - NB_ERR for other errors.
+ */
+ int (*modify)(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource);
+
+ /*
+ * Configuration callback.
+ *
+ * A presence container, list entry, leaf-list entry or optional leaf
+ * has been deleted.
+ *
+ * The callback is supposed to delete the entire configuration object,
+ * including its children when they exist.
+ *
+ * event
+ * The transaction phase. Refer to the documentation comments of
+ * nb_event for more details.
+ *
+ * dnode
+ * libyang data node that is being deleted.
+ *
+ * Returns:
+ * - NB_OK on success.
+ * - NB_ERR_VALIDATION when a validation error occurred.
+ * - NB_ERR_INCONSISTENCY when an inconsistency was detected.
+ * - NB_ERR for other errors.
+ */
+ int (*delete)(enum nb_event event, const struct lyd_node *dnode);
+
+ /*
+ * Configuration callback.
+ *
+ * A list entry or leaf-list entry has been moved. Only applicable when
+ * the "ordered-by user" statement is present.
+ *
+ * event
+ * The transaction phase. Refer to the documentation comments of
+ * nb_event for more details.
+ *
+ * dnode
+ * libyang data node that is being moved.
+ *
+ * Returns:
+ * - NB_OK on success.
+ * - NB_ERR_VALIDATION when a validation error occurred.
+ * - NB_ERR_INCONSISTENCY when an inconsistency was detected.
+ * - NB_ERR for other errors.
+ */
+ int (*move)(enum nb_event event, const struct lyd_node *dnode);
+
+Since skeleton northbound callbacks are generated automatically by the
+*gen_northbound_callbacks* tool, the developer doesn’t need to worry
+about which callbacks need to be implemented.
+
+ NOTE: once a daemon starts, it reads its YANG modules and validates
+ that all required northbound callbacks were implemented. If any
+ northbound callback is missing, an error is logged and the program
+ exists.
+
+Transaction phases
+^^^^^^^^^^^^^^^^^^
+
+Configuration transactions and their phases were described in detail in
+the [[Architecture]] page. Here’s the definition of the ``nb_event``
+enumeration as defined in the *lib/northbound.h* file:
+
+.. code:: c
+
+ /* Northbound events. */
+ enum nb_event {
+ /*
+ * The configuration callback is supposed to verify that the changes are
+ * valid and can be applied.
+ */
+ NB_EV_VALIDATE,
+
+ /*
+ * The configuration callback is supposed to prepare all resources
+ * required to apply the changes.
+ */
+ NB_EV_PREPARE,
+
+ /*
+ * Transaction has failed, the configuration callback needs to release
+ * all resources previously allocated.
+ */
+ NB_EV_ABORT,
+
+ /*
+ * The configuration changes need to be applied. The changes can't be
+ * rejected at this point (errors are logged and ignored).
+ */
+ NB_EV_APPLY,
+ };
+
+When converting a CLI command, we must identify all error-prone
+operations and perform them in the ``NB_EV_PREPARE`` phase of the
+northbound callbacks. When the operation in question involves the
+allocation of a specific resource (e.g. file descriptors), we can store
+the allocated resource in the ``resource`` variable given to the
+callback. This way the allocated resource can be obtained in the other
+phases of the transaction using the same parameter.
+
+Here’s the ``create`` northbound callback associated to the
+``router rip`` command:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:ripd/instance
+ */
+ static int ripd_instance_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+ {
+ int socket;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ break;
+ case NB_EV_PREPARE:
+ socket = rip_create_socket();
+ if (socket < 0)
+ return NB_ERR_RESOURCE;
+ resource->fd = socket;
+ break;
+ case NB_EV_ABORT:
+ socket = resource->fd;
+ close(socket);
+ break;
+ case NB_EV_APPLY:
+ socket = resource->fd;
+ rip_create(socket);
+ break;
+ }
+
+ return NB_OK;
+ }
+
+Note that the socket creation is an error-prone operation since it
+depends on the underlying operating system, so the socket must be
+created during the ``NB_EV_PREPARE`` phase and stored in
+``resource->fd``. This socket is then either closed or used depending on
+the outcome of the preparation phase of the whole transaction.
+
+During the ``NB_EV_VALIDATE`` phase, the northbound callbacks must
+validate if the intended changes are valid. As an example, FRR doesn’t
+allow the operator to deconfigure active interfaces:
+
+.. code:: c
+
+ static int lib_interface_delete(enum nb_event event,
+ const struct lyd_node *dnode)
+ {
+ struct interface *ifp;
+
+ ifp = yang_dnode_get_entry(dnode);
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ zlog_warn("%s: only inactive interfaces can be deleted",
+ __func__);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if_delete(ifp);
+ break;
+ }
+
+ return NB_OK;
+ }
+
+Note however that it’s preferred to use YANG to model the validation
+constraints whenever possible. Code-level validations should be used
+only to validate constraints that can’t be modeled using the YANG
+language.
+
+Most callbacks don’t need to perform any validations nor perform any
+error-prone operations, so in these cases we can use the following
+pattern to return early if ``event`` is different than ``NB_EV_APPLY``:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:ripd/instance/distance/default
+ */
+ static int ripd_instance_distance_default_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+ {
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip->distance = yang_dnode_get_uint8(dnode, NULL);
+
+ return NB_OK;
+ }
+
+During development it’s recommend to use the *debug northbound* command
+to debug configuration transactions and see what callbacks are being
+called. Example:
+
+::
+
+ ripd# conf t
+ ripd(config)# debug northbound
+ ripd(config)# router rip
+ ripd(config-router)# allow-ecmp
+ ripd(config-router)# network eth0
+ ripd(config-router)# redistribute ospf metric 2
+ ripd(config-router)# commit
+ % Configuration committed successfully.
+
+ ripd(config-router)#
+
+Now the ripd log:
+
+::
+
+ 2018/09/23 12:43:59 RIP: northbound callback: event [validate] op [create] xpath [/frr-ripd:ripd/instance] value [(none)]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [validate] op [modify] xpath [/frr-ripd:ripd/instance/allow-ecmp] value [true]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [validate] op [create] xpath [/frr-ripd:ripd/instance/interface[.='eth0']] value [eth0]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [validate] op [create] xpath [/frr-ripd:ripd/instance/redistribute[protocol='ospf']] value [(none)]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [validate] op [modify] xpath [/frr-ripd:ripd/instance/redistribute[protocol='ospf']/metric] value [2]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [prepare] op [create] xpath [/frr-ripd:ripd/instance] value [(none)]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [prepare] op [modify] xpath [/frr-ripd:ripd/instance/allow-ecmp] value [true]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [prepare] op [create] xpath [/frr-ripd:ripd/instance/interface[.='eth0']] value [eth0]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [prepare] op [create] xpath [/frr-ripd:ripd/instance/redistribute[protocol='ospf']] value [(none)]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [prepare] op [modify] xpath [/frr-ripd:ripd/instance/redistribute[protocol='ospf']/metric] value [2]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [apply] op [create] xpath [/frr-ripd:ripd/instance] value [(none)]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [apply] op [modify] xpath [/frr-ripd:ripd/instance/allow-ecmp] value [true]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [apply] op [create] xpath [/frr-ripd:ripd/instance/interface[.='eth0']] value [eth0]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [apply] op [create] xpath [/frr-ripd:ripd/instance/redistribute[protocol='ospf']] value [(none)]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [apply] op [modify] xpath [/frr-ripd:ripd/instance/redistribute[protocol='ospf']/metric] value [2]
+ 2018/09/23 12:43:59 RIP: northbound callback: event [apply] op [apply_finish] xpath [/frr-ripd:ripd/instance/redistribute[protocol='ospf']] value [(null)]
+
+Getting the data
+^^^^^^^^^^^^^^^^
+
+One parameter that is common to all northbound configuration callbacks
+is the ``dnode`` parameter. This is a libyang data node structure that
+contains information relative to the configuration change that is being
+performed. For ``create`` callbacks, it contains the configuration node
+that is being added. For ``delete`` callbacks, it contains the
+configuration node that is being deleted. For ``modify`` callbacks, it
+contains the configuration node that is being modified.
+
+In order to get the actual data value out of the ``dnode`` variable, we
+need to use the ``yang_dnode_get_*()`` wrappers documented in
+*lib/yang_wrappers.h*.
+
+The advantage of passing a ``dnode`` structure to the northbound
+callbacks is that the whole candidate being committed is made available,
+so the callbacks can obtain values from other portions of the
+configuration if necessary. This can be done by providing an xpath
+expression to the second parameter of the ``yang_dnode_get_*()``
+wrappers to specify the element we want to get. The example below shows
+a callback that gets the values of two leaves that are part of the same
+list entry:
+
+.. code:: c
+
+ static int
+ ripd_instance_redistribute_metric_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+ {
+ int type;
+ uint8_t metric;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ type = yang_dnode_get_enum(dnode, "../protocol");
+ metric = yang_dnode_get_uint8(dnode, NULL);
+
+ rip->route_map[type].metric_config = true;
+ rip->route_map[type].metric = metric;
+ rip_redistribute_conf_update(type);
+
+ return NB_OK;
+ }
+
+..
+
+ NOTE: if the wrong ``yang_dnode_get_*()`` wrapper is used, the code
+ will log an error and abort. An example would be using
+ ``yang_dnode_get_enum()`` to get the value of a boolean data node.
+
+No need to check if the configuration value has changed
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A common pattern in CLI commands is this:
+
+.. code:: c
+
+ DEFUN (...)
+ {
+ [snip]
+ if (new_value == old_value)
+ return CMD_SUCCESS;
+ [snip]
+ }
+
+Several commands need to check if the new value entered by the user is
+the same as the one currently configured. Then, if yes, ignore the
+command since nothing was changed.
+
+The northbound callbacks on the other hand don’t need to perform this
+check since they act on effective configuration changes. Using the CLI
+as an example, if the operator enters the same command multiple times,
+the northbound layer will detect that nothing has changed in the
+configuration and will avoid calling the northbound callbacks
+unnecessarily.
+
+In some cases, however, it might be desirable to check for
+inconsistencies and notify the northbound when that happens:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:ripd/instance/interface
+ */
+ static int ripd_instance_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+ {
+ const char *ifname;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifname = yang_dnode_get_string(dnode, NULL);
+
+ return rip_enable_if_add(ifname);
+ }
+
+.. code:: c
+
+ /* Add interface to rip_enable_if. */
+ int rip_enable_if_add(const char *ifname)
+ {
+ int ret;
+
+ ret = rip_enable_if_lookup(ifname);
+ if (ret >= 0)
+ return NB_ERR_INCONSISTENCY;
+
+ vector_set(rip_enable_interface,
+ XSTRDUP(MTYPE_RIP_INTERFACE_STRING, ifname));
+
+ rip_enable_apply_all(); /* TODOVJ */
+
+ return NB_OK;
+ }
+
+In the example above, the ``rip_enable_if_add()`` function should never
+return ``NB_ERR_INCONSISTENCY`` in normal conditions. This is because
+the northbound layer guarantees that the same interface will never be
+added more than once (except when it’s removed and re-added again). But
+to be on the safe side it’s probably wise to check for internal
+inconsistencies to ensure everything is working as expected.
+
+Default values
+^^^^^^^^^^^^^^
+
+Whenever creating a new presence-container or list entry, it’s usually
+necessary to initialize certain variables to their default values. FRR
+most of the time uses special constants for that purpose
+(e.g. ``RIP_DEFAULT_METRIC_DEFAULT``, ``DFLT_BGP_HOLDTIME``, etc). Now
+that we have YANG models, we want to fetch the default values from these
+models instead. This will allow us to changes default values smoothly
+without needing to touch the code. Better yet, it will allow users to
+create YANG deviations to define custom default values easily.
+
+To fetch default values from the loaded YANG models, use the
+``yang_get_default_*()`` wrapper functions
+(e.g. ``yang_get_default_bool()``) documented in *lib/yang_wrappers.h*.
+
+Example:
+
+.. code:: c
+
+ int rip_create(int socket)
+ {
+ rip = XCALLOC(MTYPE_RIP, sizeof(struct rip));
+
+ /* Set initial values. */
+ rip->ecmp = yang_get_default_bool("%s/allow-ecmp", RIP_INSTANCE);
+ rip->default_metric =
+ yang_get_default_uint8("%s/default-metric", RIP_INSTANCE);
+ [snip]
+ }
+
+Configuration options are edited individually
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Several CLI commands edit multiple configuration options at the same
+time. Some examples taken from ripd:
+
+* ``timers basic (5-2147483647) (5-2147483647) (5-2147483647)``
+ * */frr-ripd:ripd/instance/timers/flush-interval*
+ * */frr-ripd:ripd/instance/timers/holddown-interval*
+ * */frr-ripd:ripd/instance/timers/update-interval*
+
+* ``distance (1-255) A.B.C.D/M [WORD]``
+ * */frr-ripd:ripd/instance/distance/source/prefix*
+ * */frr-ripd:ripd/instance/distance/source/distance*
+ * */frr-ripd:ripd/instance/distance/source/access-list*
+
+In the new northbound model, there’s one or more separate callbacks for
+each configuration option. This usually has implications when converting
+code from CLI commands to the northbound commands. An example of this is
+the following commit from ripd:
+`7cf2f2eaf <https://github.com/opensourcerouting/frr/commit/7cf2f2eaf43ef5df294625d1ab4c708db8293510>`__.
+The ``rip_distance_set()`` and ``rip_distance_unset()`` functions were
+torn apart and their code split into a few different callbacks.
+
+For lists and presence-containers, it’s possible to use the
+``yang_dnode_set_entry()`` function to attach user data to a libyang
+data node, and then retrieve this value in the other callbacks (for the
+same node or any of its children) using the ``yang_dnode_get_entry()``
+function. Example:
+
+.. code:: c
+
+ static int ripd_instance_distance_source_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+ {
+ struct prefix_ipv4 prefix;
+ struct route_node *rn;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ yang_dnode_get_ipv4p(&prefix, dnode, "./prefix");
+
+ /* Get RIP distance node. */
+ rn = route_node_get(rip_distance_table, (struct prefix *)&prefix);
+ rn->info = rip_distance_new();
+ yang_dnode_set_entry(dnode, rn);
+
+ return NB_OK;
+ }
+
+.. code:: c
+
+ static int
+ ripd_instance_distance_source_distance_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+ {
+ struct route_node *rn;
+ uint8_t distance;
+ struct rip_distance *rdistance;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ /* Set distance value. */
+ rn = yang_dnode_get_entry(dnode);
+ distance = yang_dnode_get_uint8(dnode, NULL);
+ rdistance = rn->info;
+ rdistance->distance = distance;
+
+ return NB_OK;
+ }
+
+Commands that edit multiple configuration options at the same time can
+also use the ``apply_finish`` optional callback, documented as follows
+in the *lib/northbound.h* file:
+
+.. code:: c
+
+ /*
+ * Optional configuration callback for YANG lists and containers.
+ *
+ * The 'apply_finish' callbacks are called after all other callbacks
+ * during the apply phase (NB_EV_APPLY). These callbacks are called only
+ * under one of the following two cases:
+ * * The container or a list entry has been created;
+ * * Any change is made within the descendants of the list entry or
+ * container (e.g. a child leaf was modified, created or deleted).
+ *
+ * This callback is useful in the cases where a single event should be
+ * triggered regardless if the container or list entry was changed once
+ * or multiple times.
+ *
+ * dnode
+ * libyang data node from the YANG list or container.
+ */
+ void (*apply_finish)(const struct lyd_node *dnode);
+
+Here’s an example of how this callback can be used:
+
+.. code:: c
+
+ /*
+ * XPath: /frr-ripd:ripd/instance/timers/
+ */
+ static void ripd_instance_timers_apply_finish(const struct lyd_node *dnode)
+ {
+ /* Reset update timer thread. */
+ rip_event(RIP_UPDATE_EVENT, 0);
+ }
+
+.. code:: c
+
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers",
+ .cbs.apply_finish = ripd_instance_timers_apply_finish,
+ .cbs.cli_show = cli_show_rip_timers,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers/flush-interval",
+ .cbs.modify = ripd_instance_timers_flush_interval_modify,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers/holddown-interval",
+ .cbs.modify = ripd_instance_timers_holddown_interval_modify,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers/update-interval",
+ .cbs.modify = ripd_instance_timers_update_interval_modify,
+ },
+
+In this example, we want to call the ``rip_event()`` function only once
+regardless if all RIP timers were modified or only one of them. Without
+the ``apply_finish`` callback we’d need to call ``rip_event()`` in the
+``modify`` callback of each timer (a YANG leaf), resulting in redundant
+call to the ``rip_event()`` function if multiple timers are changed at
+once.
+
+Bonus: libyang user types
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When writing YANG modules, it’s advisable to create derived types for
+data types that are used on multiple places (e.g. MAC addresses, IS-IS
+networks, etc). Here’s how `RFC
+7950 <https://tools.ietf.org/html/rfc7950#page-25>`__ defines derived
+types: > YANG can define derived types from base types using the
+“typedef” > statement. A base type can be either a built-in type or a
+derived > type, allowing a hierarchy of derived types. > > A derived
+type can be used as the argument for the “type” statement. > > YANG
+Example: > > typedef percent { > type uint8 { > range “0 .. 100”; > } >
+} > > leaf completed { > type percent; > }
+
+Derived types are essentially built-in types with imposed restrictions.
+As an example, the ``ipv4-address`` derived type from IETF is defined
+using the ``string`` built-in type with a ``pattern`` constraint (a
+regular expression):
+
+::
+
+ typedef ipv4-address {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ + '(%[\p{N}\p{L}]+)?';
+ }
+ description
+ "The ipv4-address type represents an IPv4 address in
+ dotted-quad notation. The IPv4 address may include a zone
+ index, separated by a % sign.
+
+ The zone index is used to disambiguate identical address
+ values. For link-local addresses, the zone index will
+ typically be the interface index number or the name of an
+ interface. If the zone index is not present, the default
+ zone of the device will be used.
+
+ The canonical format for the zone index is the numerical
+ format";
+ }
+
+Sometimes, however, it’s desirable to have a binary representation of
+the derived type that is different from the associated built-in type.
+Taking the ``ipv4-address`` example above, it would be more convenient
+to manipulate this YANG type using ``in_addr`` structures instead of
+strings. libyang allow us to do that using the user types plugin:
+https://netopeer.liberouter.org/doc/libyang/master/howtoschemaplugins.html#usertypes
+
+Here’s how the the ``ipv4-address`` derived type is implemented in FRR
+(*yang/libyang_plugins/frr_user_types.c*):
+
+.. code:: c
+
+ static int ipv4_address_store_clb(const char *type_name, const char *value_str,
+ lyd_val *value, char **err_msg)
+ {
+ value->ptr = malloc(sizeof(struct in_addr));
+ if (!value->ptr)
+ return 1;
+
+ if (inet_pton(AF_INET, value_str, value->ptr) != 1) {
+ free(value->ptr);
+ return 1;
+ }
+
+ return 0;
+ }
+
+.. code:: c
+
+ struct lytype_plugin_list frr_user_types[] = {
+ {"ietf-inet-types", "2013-07-15", "ipv4-address",
+ ipv4_address_store_clb, free},
+ {"ietf-inet-types", "2013-07-15", "ipv4-address-no-zone",
+ ipv4_address_store_clb, free},
+ [snip]
+ {NULL, NULL, NULL, NULL, NULL} /* terminating item */
+ };
+
+Now, in addition to the string representation of the data value, libyang
+will also store the data in the binary format we specified (an
+``in_addr`` structure).
+
+Whenever a new derived type is implemented in FRR, it’s also recommended
+to write new wrappers in the *lib/yang_wrappers.c* file
+(e.g. ``yang_dnode_get_ipv4()``, ``yang_get_default_ipv4()``, etc).
+
+Step 5: rewrite the CLI commands as dumb wrappers around the northbound callbacks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once the northbound callbacks are implemented, we need to rewrite the
+associated CLI commands on top of the northbound layer. This is the
+easiest part of the retrofitting process.
+
+For protocol daemons, it’s recommended to put all CLI commands on a
+separate C file (e.g. *ripd/rip_cli.c*). This helps to keep the code
+more clean by separating the main protocol code from the user interface.
+It should also help when moving the CLI to a separate program in the
+future.
+
+For libfrr commands, it’s not possible to centralize all commands in a
+single file because the *extract.pl* script from *vtysh* treats commands
+differently depending on the file in which they are defined (e.g. DEFUNs
+from *lib/routemap.c* are installed using the ``VTYSH_RMAP_SHOW`` constant,
+which identifies the daemons that support route-maps). In this case, the
+CLI commands should be rewritten but maintained in the same file.
+
+Since all CLI configuration commands from FRR will need to be rewritten,
+this is an excellent opportunity to rework this part of the code to make
+the commands easier to maintain and extend. These are the three main
+recommendations:
+
+#. Always use DEFPY instead of DEFUN to improve code readability
+#. Always try to join multiple DEFUNs into a single DEFPY whenever possible. As
+ an example, there’s no need to have both ``distance (1-255) A.B.C.D/M`` and
+ ``distance (1-255) A.B.C.D/M WORD`` when a single ``distance (1-255)
+ A.B.C.D/M [WORD]`` would suffice.
+#. When making a negative form of a command, put ``[no]`` in the positive form
+ and use ``![...]`` to mark portions of the command that should be optional
+ only in the ``no`` version.
+
+To rewrite a CLI command as a dumb wrapper around the northbound
+callbacks, use the ``nb_cli_cfg_change()`` function. This function
+accepts as a parameter an array of ``cli_config_change`` structures that
+specify the changes that need to performed on the candidate
+configuration. Here’s the declaration of this structure (taken from the
+``lib/northbound_cli.h`` file):
+
+.. code:: c
+
+ struct cli_config_change {
+ /*
+ * XPath (absolute or relative) of the configuration option being
+ * edited.
+ */
+ char xpath[XPATH_MAXLEN];
+
+ /*
+ * Operation to apply (either NB_OP_CREATE, NB_OP_MODIFY or
+ * NB_OP_DESTROY).
+ */
+ enum nb_operation operation;
+
+ /*
+ * New value of the configuration option. Should be NULL for typeless
+ * YANG data (e.g. presence-containers). For convenience, NULL can also
+ * be used to restore a leaf to its default value.
+ */
+ const char *value;
+ };
+
+The ``nb_cli_cfg_change()`` function positions the CLI command on top on
+top of the northbound layer. Instead of changing the running
+configuration directly, this function changes the candidate
+configuration instead, as described in the [[Transactional CLI]] page.
+When the transactional CLI is not in use (i.e. the default mode), then
+``nb_cli_cfg_change()`` performs an implicit ``commit`` operation after
+changing the candidate configuration.
+
+ NOTE: the ``nb_cli_cfg_change()`` function clones the candidate
+ configuration before actually editing it. This way, if any error
+ happens during the editing, the original candidate is restored to
+ avoid inconsistencies. Either all changes from the configuration
+ command are performed successfully or none are. It’s like a
+ mini-transaction but happening on the candidate configuration (thus
+ the northbound callbacks are not involved).
+
+Other important details to keep in mind while rewriting the CLI
+commands:
+
+* ``nb_cli_cfg_change()`` returns CLI errors codes (e.g. ``CMD_SUCCESS``,
+ ``CMD_WARNING``), so the return value of this function can be used as the
+ return value of CLI commands.
+
+* Calls to ``VTY_PUSH_CONTEXT`` and ``VTY_PUSH_CONTEXT_SUB`` should be converted
+ to calls to ``VTY_PUSH_XPATH``. Similarly, the following macros aren’t
+ necessary anymore and can be removed:
+
+ * ``VTY_DECLVAR_CONTEXT``
+ * ``VTY_DECLVAR_CONTEXT_SUB``
+ * ``VTY_GET_CONTEXT``
+ * ``VTY_CHECK_CONTEXT``.
+
+ The ``nb_cli_cfg_change()`` functions uses the ``VTY_CHECK_XPATH`` macro to
+ check if the data node being edited still exists before doing anything else.
+
+The examples below provide additional details about how the conversion
+should be done.
+
+Example 1
+^^^^^^^^^
+
+In this first example, the *router rip* command becomes a dumb wrapper
+around the ``ripd_instance_create()`` callback. Note that we don’t need
+to check if the ``/frr-ripd:ripd/instance`` data path already exists
+before trying to create it. The northbound will detect when this
+presence-container already exists and do nothing. The
+``VTY_PUSH_XPATH()`` macro is used to change the vty node and set the
+context for other commands under *router rip*.
+
+.. code:: c
+
+ DEFPY_NOSH (router_rip,
+ router_rip_cmd,
+ "router rip",
+ "Enable a routing process\n"
+ "Routing Information Protocol (RIP)\n")
+ {
+ int ret;
+
+ struct cli_config_change changes[] = {
+ {
+ .xpath = "/frr-ripd:ripd/instance",
+ .operation = NB_OP_CREATE,
+ .value = NULL,
+ },
+ };
+
+ ret = nb_cli_cfg_change(vty, NULL, changes, array_size(changes));
+ if (ret == CMD_SUCCESS)
+ VTY_PUSH_XPATH(RIP_NODE, changes[0].xpath);
+
+ return ret;
+ }
+
+Example 2
+^^^^^^^^^
+
+Here we can see the use of relative xpaths (starting with ``./``), which
+are more convenient that absolute xpaths (which would be
+``/frr-ripd:ripd/instance/default-metric`` in this example). This is
+possible because the use of ``VTY_PUSH_XPATH()`` in the *router rip*
+command set the vty base xpath to ``/frr-ripd:ripd/instance``.
+
+.. code:: c
+
+ DEFPY (rip_default_metric,
+ rip_default_metric_cmd,
+ "default-metric (1-16)",
+ "Set a metric of redistribute routes\n"
+ "Default metric\n")
+ {
+ struct cli_config_change changes[] = {
+ {
+ .xpath = "./default-metric",
+ .operation = NB_OP_MODIFY,
+ .value = default_metric_str,
+ },
+ };
+
+ return nb_cli_cfg_change(vty, NULL, changes, array_size(changes));
+ }
+
+In the command below we the ``value`` to NULL to indicate that we want
+to set this leaf to its default value. This is better than hardcoding
+the default value because the default might change in the future. Also,
+users might define custom defaults by using YANG deviations, so it’s
+better to write code that works correctly regardless of the default
+values defined in the YANG models.
+
+.. code:: c
+
+ DEFPY (no_rip_default_metric,
+ no_rip_default_metric_cmd,
+ "no default-metric [(1-16)]",
+ NO_STR
+ "Set a metric of redistribute routes\n"
+ "Default metric\n")
+ {
+ struct cli_config_change changes[] = {
+ {
+ .xpath = "./default-metric",
+ .operation = NB_OP_MODIFY,
+ .value = NULL,
+ },
+ };
+
+ return nb_cli_cfg_change(vty, NULL, changes, array_size(changes));
+ }
+
+Example 3
+^^^^^^^^^
+
+This example shows how one command can change multiple leaves at the
+same time.
+
+.. code:: c
+
+ DEFPY (rip_timers,
+ rip_timers_cmd,
+ "timers basic (5-2147483647)$update (5-2147483647)$timeout (5-2147483647)$garbage",
+ "Adjust routing timers\n"
+ "Basic routing protocol update timers\n"
+ "Routing table update timer value in second. Default is 30.\n"
+ "Routing information timeout timer. Default is 180.\n"
+ "Garbage collection timer. Default is 120.\n")
+ {
+ struct cli_config_change changes[] = {
+ {
+ .xpath = "./timers/update-interval",
+ .operation = NB_OP_MODIFY,
+ .value = update_str,
+ },
+ {
+ .xpath = "./timers/holddown-interval",
+ .operation = NB_OP_MODIFY,
+ .value = timeout_str,
+ },
+ {
+ .xpath = "./timers/flush-interval",
+ .operation = NB_OP_MODIFY,
+ .value = garbage_str,
+ },
+ };
+
+ return nb_cli_cfg_change(vty, NULL, changes, array_size(changes));
+ }
+
+Example 4
+^^^^^^^^^
+
+This example shows how to create a list entry:
+
+.. code:: c
+
+ DEFPY (rip_distance_source,
+ rip_distance_source_cmd,
+ "distance (1-255) A.B.C.D/M$prefix [WORD$acl]",
+ "Administrative distance\n"
+ "Distance value\n"
+ "IP source prefix\n"
+ "Access list name\n")
+ {
+ char xpath_list[XPATH_MAXLEN];
+ struct cli_config_change changes[] = {
+ {
+ .xpath = ".",
+ .operation = NB_OP_CREATE,
+ },
+ {
+ .xpath = "./distance",
+ .operation = NB_OP_MODIFY,
+ .value = distance_str,
+ },
+ {
+ .xpath = "./access-list",
+ .operation = acl ? NB_OP_MODIFY : NB_OP_DESTROY,
+ .value = acl,
+ },
+ };
+
+ snprintf(xpath_list, sizeof(xpath_list), "./distance/source[prefix='%s']",
+ prefix_str);
+
+ return nb_cli_cfg_change(vty, xpath_list, changes, array_size(changes));
+ }
+
+The ``xpath_list`` variable is used to hold the xpath that identifies
+the list entry. The keys of the list entry should be embedded in this
+xpath and don’t need to be part of the array of configuration changes.
+All entries from the ``changes`` array use relative xpaths which are
+based on the xpath of the list entry.
+
+The ``access-list`` optional leaf can be either modified or deleted
+depending whether the optional *WORD* parameter is present or not.
+
+When deleting a list entry, all non-key leaves can be ignored:
+
+.. code:: c
+
+ DEFPY (no_rip_distance_source,
+ no_rip_distance_source_cmd,
+ "no distance (1-255) A.B.C.D/M$prefix [WORD$acl]",
+ NO_STR
+ "Administrative distance\n"
+ "Distance value\n"
+ "IP source prefix\n"
+ "Access list name\n")
+ {
+ char xpath_list[XPATH_MAXLEN];
+ struct cli_config_change changes[] = {
+ {
+ .xpath = ".",
+ .operation = NB_OP_DESTROY,
+ },
+ };
+
+ snprintf(xpath_list, sizeof(xpath_list), "./distance/source[prefix='%s']",
+ prefix_str);
+
+ return nb_cli_cfg_change(vty, xpath_list, changes, 1);
+ }
+
+Example 5
+^^^^^^^^^
+
+This example shows a DEFPY statement that performs two validations
+before calling ``nb_cli_cfg_change()``:
+
+.. code:: c
+
+ DEFPY (ip_rip_authentication_string,
+ ip_rip_authentication_string_cmd,
+ "ip rip authentication string LINE$password",
+ IP_STR
+ "Routing Information Protocol\n"
+ "Authentication control\n"
+ "Authentication string\n"
+ "Authentication string\n")
+ {
+ struct cli_config_change changes[] = {
+ {
+ .xpath = "./frr-ripd:rip/authentication/password",
+ .operation = NB_OP_MODIFY,
+ .value = password,
+ },
+ };
+
+ if (strlen(password) > 16) {
+ vty_out(vty,
+ "%% RIPv2 authentication string must be shorter than 16\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (yang_dnode_exists(vty->candidate_config->dnode, "%s%s",
+ VTY_GET_XPATH,
+ "/frr-ripd:rip/authentication/key-chain")) {
+ vty_out(vty, "%% key-chain configuration exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return nb_cli_cfg_change(vty, NULL, changes, array_size(changes));
+ }
+
+These two validations are not strictly necessary since the configuration
+change is validated using libyang afterwards. The issue with the libyang
+validation is that the error messages from libyang are too verbose:
+
+::
+
+ ripd# conf t
+ ripd(config)# interface eth0
+ ripd(config-if)# ip rip authentication string XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ % Failed to edit candidate configuration.
+
+ Value "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" does not satisfy the constraint "1..16" (range, length, or pattern).
+ Failed to create node "authentication-password" as a child of "rip".
+ YANG path: /frr-interface:lib/interface[name='eth0'][vrf='Default-IP-Routing-Table']/frr-ripd:rip/authentication-password
+
+On the other hand, the original error message from ripd is much cleaner:
+
+::
+
+ ripd# conf t
+ ripd(config)# interface eth0
+ ripd(config-if)# ip rip authentication string XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ % RIPv2 authentication string must be shorter than 16
+
+The second validation is a bit more complex. If we try to create the
+``authentication/password`` leaf when the ``authentication/key-chain``
+leaf already exists (both are under a YANG *choice* statement), libyang
+will automatically delete the ``authentication/key-chain`` and create
+``authentication/password`` on its place. This is different from the
+original ripd behavior where the *ip rip authentication key-chain*
+command must be removed before configuring the *ip rip authentication
+string* command.
+
+In the spirit of not introducing any backward-incompatible changes in
+the CLI, converted commands should retain some of their validation
+checks to preserve their original behavior.
+
+Step 6: implement the ``cli_show`` callbacks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The traditional method used by FRR to display the running configuration
+consists of looping through all CLI nodes all call their ``func``
+callbacks one by one, which in turn read the configuration from internal
+variables and dump them to the terminal in the form of CLI commands.
+
+The problem with this approach is twofold. First, since the callbacks
+read the configuration from internal variables, they can’t display
+anything other than the running configuration. Second, they don’t have
+the ability to display default values when requested by the user
+(e.g. *show configuration candidate with-defaults*).
+
+The new northbound architecture solves these problems by introducing a
+new callback: ``cli_show``. Here’s the signature of this function (taken
+from the *lib/northbound.h* file):
+
+.. code:: c
+
+ /*
+ * Optional callback to show the CLI command associated to the given
+ * YANG data node.
+ *
+ * vty
+ * the vty terminal to dump the configuration to
+ *
+ * dnode
+ * libyang data node that should be shown in the form of a CLI
+ * command
+ *
+ * show_defaults
+ * specify whether to display default configuration values or not.
+ * This parameter can be ignored most of the time since the
+ * northbound doesn't call this callback for default leaves or
+ * non-presence containers that contain only default child nodes.
+ * The exception are commands associated to multiple configuration
+ * options, in which case it might be desirable to hide one or more
+ * parts of the command when this parameter is set to false.
+ */
+ void (*cli_show)(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+
+One of the main differences to the old CLI ``func`` callbacks is that
+the ``cli_show`` callbacks are associated to YANG data paths and not to
+CLI nodes. This means we can define one separate callback for each CLI
+command, making the code more modular and easier to maintain (among
+other advantages that will be more clear later). For enhanced code
+readability, it’s recommended to position the ``cli_show`` callbacks
+immediately after their associated command definitions (DEFPYs).
+
+The ``cli_show`` callbacks are used by the ``nb_cli_show_config_cmds()``
+function to display configurations stored inside ``nb_config``
+structures. The configuration being displayed can be anything from the
+running configuration (*show configuration running*), a candidate
+configuration (*show configuration candidate*) or a rollback
+configuration (*show configuration transaction (1-4294967296)*). The
+``nb_cli_show_config_cmds()`` function works by iterating over all data
+nodes from the given configuration and calling the ``cli_show`` callback
+for the nodes where it’s defined. If a list has dozens of entries, the
+``cli_show`` callback associated to this list will be called multiple
+times with the ``dnode`` parameter pointing to different list entries on
+each iteration.
+
+For backward compatibility with the *show running-config* command, we
+can’t get rid of the CLI ``func`` callbacks at this point in time.
+However, we can make the CLI ``func`` callbacks call the corresponding
+``cli_show`` callbacks to avoid code duplication. The
+``nb_cli_show_dnode_cmds()`` function can be used for that purpose. Once
+the CLI retrofitting process finishes for all FRR daemons, we can remove
+the legacy CLI ``func`` callbacks and turn *show running-config* into a
+shorthand for *show configuration running*.
+
+Regarding displaying configuration with default values, this is
+something that is taken care of by the ``nb_cli_show_config_cmds()``
+function itself. When the *show configuration* command is used without
+the *with-defaults* option, ``nb_cli_show_config_cmds()`` will skip
+calling ``cli_show`` callbacks for data nodes that contain only default
+values (e.g. default leaves or non-presence containers that contain only
+default child nodes). There are however some exceptional cases where the
+implementer of the ``cli_show`` callback should take into consideration
+if default values should be displayed or not. This and other concepts
+will be explained in more detail in the examples below.
+
+.. _example-1-1:
+
+Example 1
+^^^^^^^^^
+
+Command: ``default-metric (1-16)``
+
+YANG representation:
+
+.. code:: yang
+
+ leaf default-metric {
+ type uint8 {
+ range "1..16";
+ }
+ default "1";
+ description
+ "Default metric of redistributed routes.";
+ }
+
+Placement of the ``cli_show`` callback:
+
+.. code:: diff
+
+ {
+ .xpath = "/frr-ripd:ripd/instance/default-metric",
+ .cbs.modify = ripd_instance_default_metric_modify,
+ + .cbs.cli_show = cli_show_rip_default_metric,
+ },
+
+Implementation of the ``cli_show`` callback:
+
+.. code:: c
+
+ void cli_show_rip_default_metric(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+ {
+ vty_out(vty, " default-metric %s\n",
+ yang_dnode_get_string(dnode, NULL));
+ }
+
+In this first example, the *default-metric* command was modeled using a
+YANG leaf, and we added a new ``cli_show`` callback attached to the YANG
+path of this leaf.
+
+The callback makes use of the ``yang_dnode_get_string()`` function to
+obtain the string value of the configuration option. The following would
+also be possible:
+
+.. code:: c
+
+ vty_out(vty, " default-metric %u\n",
+ yang_dnode_get_uint8(dnode, NULL));
+
+Both options are possible because libyang stores both a binary
+representation and a textual representation of all values stored in a
+data node (``lyd_node``). For simplicity, it’s recommended to always use
+``yang_dnode_get_string()`` in the ``cli_show`` callbacks.
+
+.. _example-2-1:
+
+Example 2
+^^^^^^^^^
+
+Command: ``router rip``
+
+YANG representation:
+
+.. code:: yang
+
+ container instance {
+ presence "Present if the RIP protocol is enabled.";
+ description
+ "RIP routing instance.";
+ [snip]
+ }
+
+Placement of the ``cli_show`` callback:
+
+.. code:: diff
+
+ {
+ .xpath = "/frr-ripd:ripd/instance",
+ .cbs.create = ripd_instance_create,
+ .cbs.delete = ripd_instance_delete,
+ + .cbs.cli_show = cli_show_router_rip,
+ },
+
+Implementation of the ``cli_show`` callback:
+
+.. code:: c
+
+ void cli_show_router_rip(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+ {
+ vty_out(vty, "!\n");
+ vty_out(vty, "router rip\n");
+ }
+
+In this example, the ``cli_show`` callback doesn’t need to obtain any
+value from the ``dnode`` parameter since presence-containers don’t hold
+any data (apart from their child nodes, but they have their own
+``cli_show`` callbacks).
+
+.. _example-3-1:
+
+Example 3
+^^^^^^^^^
+
+Command: ``timers basic (5-2147483647) (5-2147483647) (5-2147483647)``
+
+YANG representation:
+
+.. code:: yang
+
+ container timers {
+ description
+ "Settings of basic timers";
+ leaf flush-interval {
+ type uint32 {
+ range "5..2147483647";
+ }
+ units "seconds";
+ default "120";
+ description
+ "Interval before a route is flushed from the routing
+ table.";
+ }
+ leaf holddown-interval {
+ type uint32 {
+ range "5..2147483647";
+ }
+ units "seconds";
+ default "180";
+ description
+ "Interval before better routes are released.";
+ }
+ leaf update-interval {
+ type uint32 {
+ range "5..2147483647";
+ }
+ units "seconds";
+ default "30";
+ description
+ "Interval at which RIP updates are sent.";
+ }
+ }
+
+Placement of the ``cli_show`` callback:
+
+.. code:: diff
+
+ {
+ + .xpath = "/frr-ripd:ripd/instance/timers",
+ + .cbs.cli_show = cli_show_rip_timers,
+ + },
+ + {
+ .xpath = "/frr-ripd:ripd/instance/timers/flush-interval",
+ .cbs.modify = ripd_instance_timers_flush_interval_modify,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers/holddown-interval",
+ .cbs.modify = ripd_instance_timers_holddown_interval_modify,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/timers/update-interval",
+ .cbs.modify = ripd_instance_timers_update_interval_modify,
+ },
+
+Implementation of the ``cli_show`` callback:
+
+.. code:: c
+
+ void cli_show_rip_timers(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+ {
+ vty_out(vty, " timers basic %s %s %s\n",
+ yang_dnode_get_string(dnode, "./update-interval"),
+ yang_dnode_get_string(dnode, "./holddown-interval"),
+ yang_dnode_get_string(dnode, "./flush-interval"));
+ }
+
+This command is a bit different since it changes three leaves at the
+same time. This means we need to have a single ``cli_show`` callback in
+order to display the three leaves together in the same line.
+
+The new ``cli_show_rip_timers()`` callback was added attached to the
+*timers* non-presence container that groups the three leaves. Without
+the *timers* non-presence container we’d need to display the *timers
+basic* command inside the ``cli_show_router_rip()`` callback, which
+would break our requirement of having a separate ``cli_show`` callback
+for each configuration command.
+
+.. _example-4-1:
+
+Example 4
+^^^^^^^^^
+
+Command:
+``redistribute <kernel|connected|static|ospf|isis|bgp|eigrp|nhrp|table|vnc|babel|sharp> [{metric (0-16)|route-map WORD}]``
+
+YANG representation:
+
+.. code:: yang
+
+ list redistribute {
+ key "protocol";
+ description
+ "Redistributes routes learned from other routing protocols.";
+ leaf protocol {
+ type frr-route-types:frr-route-types-v4;
+ description
+ "Routing protocol.";
+ must '. != "rip"';
+ }
+ leaf route-map {
+ type string {
+ length "1..max";
+ }
+ description
+ "Applies the conditions of the specified route-map to
+ routes that are redistributed into the RIP routing
+ instance.";
+ }
+ leaf metric {
+ type uint8 {
+ range "0..16";
+ }
+ description
+ "Metric used for the redistributed route. If a metric is
+ not specified, the metric configured with the
+ default-metric attribute in RIP router configuration is
+ used. If the default-metric attribute has not been
+ configured, the default metric for redistributed routes
+ is 0.";
+ }
+ }
+
+Placement of the ``cli_show`` callback:
+
+.. code:: diff
+
+ {
+ .xpath = "/frr-ripd:ripd/instance/redistribute",
+ .cbs.create = ripd_instance_redistribute_create,
+ .cbs.delete = ripd_instance_redistribute_delete,
+ + .cbs.cli_show = cli_show_rip_redistribute,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/redistribute/route-map",
+ .cbs.modify = ripd_instance_redistribute_route_map_modify,
+ .cbs.delete = ripd_instance_redistribute_route_map_delete,
+ },
+ {
+ .xpath = "/frr-ripd:ripd/instance/redistribute/metric",
+ .cbs.modify = ripd_instance_redistribute_metric_modify,
+ .cbs.delete = ripd_instance_redistribute_metric_delete,
+ },
+
+Implementation of the ``cli_show`` callback:
+
+.. code:: c
+
+ void cli_show_rip_redistribute(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+ {
+ vty_out(vty, " redistribute %s",
+ yang_dnode_get_string(dnode, "./protocol"));
+ if (yang_dnode_exists(dnode, "./metric"))
+ vty_out(vty, " metric %s",
+ yang_dnode_get_string(dnode, "./metric"));
+ if (yang_dnode_exists(dnode, "./route-map"))
+ vty_out(vty, " route-map %s",
+ yang_dnode_get_string(dnode, "./route-map"));
+ vty_out(vty, "\n");
+ }
+
+Similar to the previous example, the *redistribute* command changes
+several leaves at the same time, and we need a single callback to
+display all leaves in a single line in accordance to the CLI command. In
+this case, the leaves are already grouped by a YANG list so there’s no
+need to add a non-presence container. The new ``cli_show`` callback was
+attached to the YANG path of the list.
+
+It’s also worth noting the use of the ``yang_dnode_exists()`` function
+to check if optional leaves exist in the configuration before displaying
+them.
+
+.. _example-5-1:
+
+Example 5
+^^^^^^^^^
+
+Command:
+``ip rip authentication mode <md5 [auth-length <rfc|old-ripd>]|text>``
+
+YANG representation:
+
+.. code:: yang
+
+ container authentication-scheme {
+ description
+ "Specify the authentication scheme for the RIP interface";
+ leaf mode {
+ type enumeration {
+ [snip]
+ }
+ default "none";
+ description
+ "Specify the authentication mode.";
+ }
+ leaf md5-auth-length {
+ when "../mode = 'md5'";
+ type enumeration {
+ [snip]
+ }
+ default "20";
+ description
+ "MD5 authentication data length.";
+ }
+ }
+
+Placement of the ``cli_show`` callback:
+
+.. code:: diff
+
+ + {
+ + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme",
+ + .cbs.cli_show = cli_show_ip_rip_authentication_scheme,
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode",
+ .cbs.modify = lib_interface_rip_authentication_scheme_mode_modify,
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length",
+ .cbs.modify = lib_interface_rip_authentication_scheme_md5_auth_length_modify,
+ .cbs.delete = lib_interface_rip_authentication_scheme_md5_auth_length_delete,
+ },
+
+Implementation of the ``cli_show`` callback:
+
+.. code:: c
+
+ void cli_show_ip_rip_authentication_scheme(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+ {
+ switch (yang_dnode_get_enum(dnode, "./mode")) {
+ case RIP_NO_AUTH:
+ vty_out(vty, " no ip rip authentication mode\n");
+ break;
+ case RIP_AUTH_SIMPLE_PASSWORD:
+ vty_out(vty, " ip rip authentication mode text\n");
+ break;
+ case RIP_AUTH_MD5:
+ vty_out(vty, " ip rip authentication mode md5");
+ if (show_defaults
+ || !yang_dnode_is_default(dnode, "./md5-auth-length")) {
+ if (yang_dnode_get_enum(dnode, "./md5-auth-length")
+ == RIP_AUTH_MD5_SIZE)
+ vty_out(vty, " auth-length rfc");
+ else
+ vty_out(vty, " auth-length old-ripd");
+ }
+ vty_out(vty, "\n");
+ break;
+ }
+ }
+
+This is the most complex ``cli_show`` callback we have in ripd. Its
+complexity comes from the following:
+
+* The ``ip rip authentication mode ...`` command changes two YANG leaves at the
+ same time.
+
+* Part of the command should be hidden when the ``show_defaults`` parameter is
+ set to false.
+
+This is the behavior we want to implement:
+
+::
+
+ ripd(config)# interface eth0
+ ripd(config-if)# ip rip authentication mode md5
+ ripd(config-if)#
+ ripd(config-if)# show configuration candidate
+ Configuration:
+ !
+ [snip]
+ !
+ interface eth0
+ ip rip authentication mode md5
+ !
+ end
+ ripd(config-if)#
+ ripd(config-if)# show configuration candidate with-defaults
+ Configuration:
+ !
+ [snip]
+ !
+ interface eth0
+ [snip]
+ ip rip authentication mode md5 auth-length old-ripd
+ !
+ end
+
+Note that ``auth-length old-ripd`` should be hidden unless the
+configuration is shown using the *with-defaults* option. This is why the
+``cli_show_ip_rip_authentication_scheme()`` callback needs to consult
+the value of the *show_defaults* parameter. It’s expected that only a
+very small minority of all ``cli_show`` callbacks will need to consult
+the *show_defaults* parameter (there’s a chance this might be the only
+case!)
+
+In the case of the *timers basic* command seen before, we need to
+display the value of all leaves even if only one of them has a value
+different from the default. Hence the ``cli_show_rip_timers()`` callback
+was able to completely ignore the *show_defaults* parameter.
+
+Step 7: consolidation
+~~~~~~~~~~~~~~~~~~~~~
+
+As mentioned in the fourth step, the northbound retrofitting process can
+happen gradually over time, since both “old” and “new” commands can
+coexist without problems. Once all commands from a given daemon were
+converted, we can proceed to the consolidation step, which consists of
+the following:
+
+* Remove the vty configuration lock, which is enabled by default in all daemons.
+ Now multiple users should be able to edit the configuration concurrently,
+ using either shared or private candidate configurations.
+
+* Reference commit: `57dccdb1
+ <https://github.com/opensourcerouting/frr/commit/57dccdb18b799556214dcfb8943e248c0bf1f6a6>`__.
+
+* Stop using the qobj infrastructure to keep track of configuration objects.
+ This is not necessary anymore, the northbound uses a similar mechanism to keep
+ track of YANG data nodes in the candidate configuration.
+
+* Reference commit: `4e6d63ce
+ <https://github.com/opensourcerouting/frr/commit/4e6d63cebd988af650c1c29d0f2e5a251c8d2e7a>`__.
+
+* Make the daemon SIGHUP handler re-read the configuration file (and ensure it’s
+ not doing anything other than that).
+
+* Reference commit: `5e57edb4
+ <https://github.com/opensourcerouting/frr/commit/5e57edb4b71ff03f9a22d9ec1412c3c5167f90cf>`__.
+
+Final Considerations
+--------------------
+
+Testing
+~~~~~~~
+
+Converting CLI commands to the new northbound model can be a complicated
+task for beginners, but the more commands one converts, the easier it
+gets. It’s highly recommended to perform as much testing as possible on
+the converted commands to reduce the likelihood of introducing
+regressions. Tools like topotests, ANVL and the `CLI
+fuzzer <https://github.com/rwestphal/frr-cli-fuzzer>`__ can be used to
+catch hidden bugs that might be present. As usual, it’s also recommended
+to use valgrind and static code analyzers to catch other types of
+problems like memory leaks.
+
+Amount of work
+~~~~~~~~~~~~~~
+
+The output below gives a rough estimate of the total number of
+configuration commands that need to be converted per daemon:
+
+.. code:: sh
+
+ $ for dir in lib zebra bgpd ospfd ospf6d isisd ripd ripngd eigrpd pimd pbrd ldpd nhrpd babeld ; do echo -n "$dir: " && cd $dir && grep -ERn "DEFUN|DEFPY" * | grep -Ev "clippy|show|clear" | wc -l && cd ..; done
+ lib: 302
+ zebra: 181
+ bgpd: 569
+ ospfd: 198
+ ospf6d: 99
+ isisd: 126
+ ripd: 64
+ ripngd: 44
+ eigrpd: 58
+ pimd: 113
+ pbrd: 9
+ ldpd: 46
+ nhrpd: 24
+ babeld: 28
+
+As it can be seen, the northbound retrofitting process will demand a lot
+of work from FRR developers and should take months to complete. Everyone
+is welcome to collaborate!
diff --git a/doc/developer/northbound/transactional-cli.rst b/doc/developer/northbound/transactional-cli.rst
new file mode 100644
index 0000000000..5c495d3a89
--- /dev/null
+++ b/doc/developer/northbound/transactional-cli.rst
@@ -0,0 +1,244 @@
+Transactional CLI
+=================
+
+.. contents:: Table of contents
+ :local:
+ :backlinks: entry
+ :depth: 1
+
+Introduction
+~~~~~~~~~~~~
+
+All FRR daemons have built-in support for the CLI, which can be accessed
+either through local telnet or via the vty socket (e.g. by using
+*vtysh*). This will not change with the introduction of the Northbound
+API. However, a new command-line option will be available for all FRR
+daemons: ``--tcli``. When given, this option makes the daemon start with
+a transactional CLI and configuration commands behave a bit different.
+Instead of editing the running configuration, they will edit the
+candidate configuration. In other words, the configuration commands
+won’t be applied immediately, that has to be done on a separate step
+using the new ``commit`` command.
+
+The transactional CLI simply leverages the new capabilities provided by
+the Northbound API and exposes the concept of candidate configurations
+to CLI users too. When the transactional mode is not used, the
+configuration commands also edit the candidate configuration, but
+there’s an implicit ``commit`` after each command.
+
+In order for the transactional CLI to work, all configuration commands
+need to be converted to the new northbound model. Commands not converted
+to the new northbound model will change the running configuration
+directly since they bypass the FRR northbound layer. For this reason,
+starting a daemon with the transactional CLI is not advisable unless all
+of its commands have already been converted. When that’s not the case,
+we can run into a situation like this:
+
+::
+
+ ospfd(config)# router ospf
+ ospfd(config-router)# ospf router-id 1.1.1.1
+ [segfault in ospfd]
+
+The segfault above can happen if ``router ospf`` edits the candidate
+configuration but ``ospf router-id 1.1.1.1`` edits the running
+configuration. The second command tries to set
+``ospf->router_id_static`` but, since the previous ``router ospf``
+command hasn’t been commited yet, the ``ospf`` global variable is set to
+NULL, which leads to the crash. Besides this problem, having a set of
+commands that edit the candidate configuration and others that edit the
+running configuration is confusing at best. The ``--tcli`` option should
+be used only by developers until the northbound retrofitting process is
+complete.
+
+Configuration modes
+~~~~~~~~~~~~~~~~~~~
+
+When using the transactional CLI (``--tcli``), FRR supports three
+different forms of the ``configure`` command:
+
+* ``configure terminal``: in this mode, a single candidate configuration is
+ shared by all users. This means that one user might delete a configuration
+ object that’s being edited by another user, in which case the CLI will detect
+ and report the problem. If one user issues the ``commit`` command, all changes
+ done by all users are committed.
+
+* ``configure private``: users have a private candidate configuration that is
+ edited separately from the other users. The ``commit`` command commits only
+ the changes done by the user.
+
+* ``configure exclusive``: similar to ``configure private``, but also locks the
+ running configuration to prevent other users from changing it. The
+ configuration lock is released when the user exits the configuration mode.
+
+When using ``configure terminal`` or ``configure private``, the
+candidate configuration being edited might become outdated if another
+user commits a different candidate configuration on another session.
+TODO: show image to illustrate the problem.
+
+New commands
+~~~~~~~~~~~~
+
+The list below contains the new CLI commands introduced by Northbound
+API. The commands are available when a daemon is started using the
+transactional CLI (``--tcli``). Currently ``vtysh`` doesn’t support any
+of these new commands.
+
+Please refer to the [[Demos]] page to see a demo of the transactional
+CLI in action.
+
+--------------
+
+``commit check``
+''''''''''''''''
+
+Check if the candidate configuration is valid or not.
+
+``commit [force] [comment LINE...]``
+''''''''''''''''''''''''''''''''''''
+
+Commit the changes done in the candidate configuration into the running
+configuration.
+
+Options:
+
+* ``force``: commit even if the candidate configuration is outdated. It’s
+ usually a better option to use the ``update`` command instead.
+
+* ``comment LINE...``: assign a comment to the configuration transaction. This
+ comment is displayed when viewing the recorded transactions in the output of
+ the ``show configuration transaction`` command.
+
+``discard``
+'''''''''''
+
+Discard the changes done in the candidate configuration.
+
+``configuration database max-transactions (1-100)``
+'''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Set the maximum number of transactions to store in the rollback log.
+
+``configuration load <file [<json|xml> [translate WORD]] FILENAME|transaction (1-4294967296)> [replace]``
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Load a new configuration into the candidate configuration. When loading
+the configuration from a file, it’s assumed that the configuration will
+be in the form of CLI commands by default. The ``json`` and ``xml``
+options can be used to load configurations in the JSON and XML formats,
+respectively. It’s also possible to load a configuration from a previous
+transaction by specifying the desired transaction ID
+(``(1-4294967296)``).
+
+Options:
+
+* ``translate WORD``: translate the JSON/XML configuration file using the YANG
+ module translator.
+
+* ``replace``: replace the candidate by the loaded configuration. The default is
+ to merge the loaded configuration into the candidate configuration.
+
+``rollback configuration (1-4294967296)``
+'''''''''''''''''''''''''''''''''''''''''
+
+Roll back the running configuration to a previous configuration
+identified by its transaction ID (``(1-4294967296)``).
+
+``show configuration candidate [<json|xml> [translate WORD]] [<with-defaults|changes>]``
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Show the candidate configuration.
+
+Options:
+
+* ``json``: show the configuration in the JSON format.
+* ``xml``: show the configuration in the XML format.
+* ``translate WORD``: translate the JSON/XML output using the YANG module translator.
+* ``with-defaults``: show default values that are hidden by default.
+* ``changes``: show only the changes done in the candidate configuration.
+
+``show configuration compare <candidate|running|transaction (1-4294967296)> <candidate|running|transaction (1-4294967296)> [<json|xml> [translate WORD]]``
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Show the difference between two different configurations.
+
+Options:
+
+* ``json``: show the configuration differences in the JSON format.
+* ``xml``: show the configuration differences in the XML format.
+* ``translate WORD``: translate the JSON/XML output using the YANG module translator.
+
+``show configuration running [<json|xml> [translate WORD]] [with-defaults]``
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Show the running configuration.
+
+Options:
+
+* ``json``: show the configuration in the JSON format.
+* ``xml``: show the configuration in the XML format.
+* ``translate WORD``: translate the JSON/XML output using the YANG module translator.
+* ``with-defaults``: show default values that are hidden by default.
+
+NOTE: ``show configuration running`` shows only the running
+configuration as known by the northbound layer. Configuration
+commands not converted to the new northbound model will not be
+displayed. To show the full running configuration, the legacy
+``show running-config`` command must be used.
+
+``show configuration transaction [(1-4294967296) [<json|xml> [translate WORD]] [changes]]``
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+When a transaction ID (``(1-4294967296)``) is given, show the
+configuration associated to the previously committed transaction.
+
+When a transaction ID is not given, show all recorded transactions in
+the rollback log.
+
+Options:
+
+* ``json``: show the configuration in the JSON format.
+* ``xml``: show the configuration in the XML format.
+* ``translate WORD``: translate the JSON/XML output using the YANG module translator.
+* ``with-defaults``: show default values that are hidden by default.
+* ``changes``: show changes compared to the previous transaction.
+
+``show yang module [module-translator WORD] [WORD <summary|tree|yang|yin>]``
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+When a YANG module is not given, show all loaded YANG modules.
+Otherwise, show detailed information about the given module.
+
+Options:
+
+* ``module-translator WORD``: change the context to modules loaded by the
+ specified YANG module translator.
+* ``summary``: display summary information about the module.
+* ``tree``: display module in the tree (RFC 8340) format.
+* ``yang``: display module in the YANG format.
+* ``yin``: display module in the YIN format.
+
+``show yang module-translator``
+'''''''''''''''''''''''''''''''
+
+Show all loaded YANG module translators.
+
+``update``
+''''''''''
+
+Rebase the candidate configuration on top of the latest running
+configuration. Conflicts are resolved automatically by giving preference
+to the changes done in the candidate configuration.
+
+The candidate configuration might be outdated if the running
+configuration was updated after the candidate was created.
+
+``yang module-translator load FILENAME``
+''''''''''''''''''''''''''''''''''''''''
+
+Load a YANG module translator from the filesystem.
+
+``yang module-translator unload WORD``
+''''''''''''''''''''''''''''''''''''''
+
+Unload a YANG module translator identified by its name.
diff --git a/doc/developer/northbound/yang-module-translator.rst b/doc/developer/northbound/yang-module-translator.rst
new file mode 100644
index 0000000000..17ae160f07
--- /dev/null
+++ b/doc/developer/northbound/yang-module-translator.rst
@@ -0,0 +1,633 @@
+YANG Module Translation
+=======================
+
+.. contents:: Table of contents
+ :local:
+ :backlinks: entry
+ :depth: 1
+
+Introduction
+------------
+
+One key requirement for the FRR northbound architecture is that it
+should be possible to configure/monitor FRR using different sets of YANG
+models. This is especially important considering that the industry
+hasn’t reached a consensus to provide a single source of standard models
+for network management. At this moment both the IETF and OpenConfig
+models are widely implemented and are unlikely to converge, at least not
+in the short term. In the ideal scenario, management applications should
+be able to use either IETF or OpenConfig models to configure and monitor
+FRR programatically (or even both at the same time!).
+
+But how can FRR support multiple sets of YANG models at the same time?
+There must be only a single source of truth that models the existing
+implementation accurately (the native models). Writing different code
+paths or callbacks for different models would be inviable, it would lead
+to a lot of duplicated code and extra maintenance overhead.
+
+In order to support different sets of YANG modules without introducing
+the overhead of writing additional code, the solution is to create a
+mechanism that dynamically translates YANG instance data between
+non-native models to native models and vice-versa. Based on this idea,
+an experimental YANG module translator was implemented within the FRR
+northbound layer. The translator works by translating XPaths at runtime
+using translation tables provided by the user. The translator itself is
+modeled using YANG and users can create translators using simple JSON
+files.
+
+A YANG module translator consists of two components: deviation modules
+and translation tables.
+
+Deviation Modules
+-----------------
+
+The first step when writing a YANG module translator is to create a
+`deviations <https://tools.ietf.org/html/rfc7950#page-131>`__ module for
+each module that is going be translated. This is necessary because in
+most cases it won’t be possible to create a perfect translator that
+covers the non-native models on their entirety. Some non-native modules
+might contain nodes that can’t be mapped to a corresponding node in the
+FRR native models. This is either because the corresponding
+functionality is not implemented in FRR or because it’s modeled in a
+different way that is incompatible.
+
+An an example, *ripd* doesn’t have BFD support yet, so we need to create
+a YANG deviation to modify the *ietf-rip* module and remove the ``bfd``
+container from it:
+
+.. code:: yang
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:interfaces/ietf-rip:interface/ietf-rip:bfd" {
+ deviate not-supported;
+ }
+
+In the example below, while both the *frr-ripd* and *ietf-rip* modules
+support RIP authentication, they model the authentication data in
+different ways, making translation not possible given the constraints of
+the current module translator. A new deviation is necessary to remove
+the ``authentication`` container from the *ietf-rip* module:
+
+.. code:: yang
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:interfaces/ietf-rip:interface/ietf-rip:authentication" {
+ deviate not-supported;
+ }
+
+..
+
+ NOTE: it should be possible to translate the
+ ``ietf-rip:authentication`` container if the *frr-ripd* module is
+ modified to model the corresponding data in a compatible way. Another
+ option is to improve the module translator to make more complex
+ translations possible, instead of requiring one-to-one XPath
+ mappings.
+
+Sometimes creating a mapping between nodes from the native and
+non-native models is possible, but the nodes have different properties
+that need to be normalized to allow the translation. In the example
+below, a YANG deviation is used to change the type and the default value
+from a node from the ``ietf-rip`` module.
+
+.. code:: yang
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:timers/ietf-rip:flush-interval" {
+ deviate replace {
+ default "120";
+ }
+ deviate replace {
+ type uint32;
+ }
+ }
+
+The deviation modules allow the management applications to know which
+parts of the custom modules (e.g. IETF/OC) can be used to configure and
+monitor FRR.
+
+In order to facilitate the process of creating YANG deviation modules,
+the *gen_yang_deviations* tool was created to automate part of the
+process. This tool creates a “not-supported” deviation for all nodes
+from the given non-native module. Example:
+
+::
+
+ $ tools/gen_yang_deviations ietf-rip > yang/ietf/frr-deviations-ietf-rip.yang
+ $ head -n 40 yang/ietf/frr-deviations-ietf-rip.yang
+ deviation "/ietf-rip:clear-rip-route" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-rip:clear-rip-route/ietf-rip:input" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-rip:clear-rip-route/ietf-rip:input/ietf-rip:rip-instance" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:originate-default-route" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:originate-default-route/ietf-rip:enabled" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:originate-default-route/ietf-rip:route-policy" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:default-metric" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:distance" {
+ deviate not-supported;
+ }
+
+ deviation "/ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol/ietf-rip:rip/ietf-rip:triggered-update-threshold" {
+ deviate not-supported;
+ }
+
+Once all existing nodes are listed in the deviation module, it’s easy to
+check the deviations that need to be removed or modified. This is more
+convenient than starting with a blank deviations module and listing
+manually all nodes that need to be deviated.
+
+After removing and/or modifying the auto-generated deviations, the next
+step is to write the module XPath translation table as we’ll see in the
+next section. Before that, it’s possible to use the *yanglint* tool to
+check how the non-native module looks like after applying the
+deviations. Example:
+
+::
+
+ $ yanglint -f tree yang/ietf/ietf-rip@2018-02-03.yang yang/ietf/frr-deviations-ietf-rip.yang
+ module: ietf-rip
+
+ augment /ietf-routing:routing/ietf-routing:control-plane-protocols/ietf-routing:control-plane-protocol:
+ +--rw rip
+ +--rw originate-default-route
+ | +--rw enabled? boolean <false>
+ +--rw default-metric? uint8 <1>
+ +--rw distance? uint8 <0>
+ +--rw timers
+ | +--rw update-interval? uint32 <30>
+ | +--rw holddown-interval? uint32 <180>
+ | +--rw flush-interval? uint32 <120>
+ +--rw interfaces
+ | +--rw interface* [interface]
+ | +--rw interface ietf-interfaces:interface-ref
+ | +--rw split-horizon? enumeration <simple>
+ +--ro ipv4
+ +--ro neighbors
+ | +--ro neighbor* [ipv4-address]
+ | +--ro ipv4-address ietf-inet-types:ipv4-address
+ | +--ro last-update? ietf-yang-types:date-and-time
+ | +--ro bad-packets-rcvd? ietf-yang-types:counter32
+ | +--ro bad-routes-rcvd? ietf-yang-types:counter32
+ +--ro routes
+ +--ro route* [ipv4-prefix]
+ +--ro ipv4-prefix ietf-inet-types:ipv4-prefix
+ +--ro next-hop? ietf-inet-types:ipv4-address
+ +--ro interface? ietf-interfaces:interface-ref
+ +--ro metric? uint8
+
+ rpcs:
+ +---x clear-rip-route
+
+..
+
+ NOTE: the same output can be obtained using the
+ ``show yang module module-translator ietf ietf-rip tree`` command in
+ FRR once the *ietf* module translator is loaded.
+
+In the example above, it can be seen that the vast majority of the
+*ietf-rip* nodes were removed because of the “not-supported” deviations.
+When a module translator is loaded, FRR calculates the coverage of the
+translator by dividing the number of YANG nodes before applying the
+deviations by the number of YANG nodes after applying the deviations.
+The calculated coverage is displayed in the output of the
+``show yang module-translator`` command:
+
+::
+
+ ripd# show yang module-translator
+ Family Module Deviations Coverage (%)
+ -----------------------------------------------------------------------
+ ietf ietf-interfaces frr-deviations-ietf-interfaces 3.92
+ ietf ietf-routing frr-deviations-ietf-routing 1.56
+ ietf ietf-rip frr-deviations-ietf-rip 13.60
+
+As it can be seen in the output above, the *ietf* module translator
+covers only ~13% of the original *ietf-rip* module. This is in part
+because the *ietf-rip* module models both RIPv2 and RIPng. Also,
+*ietf-rip.yang* contains several knobs that aren’t implemented in *ripd*
+yet (e.g. BFD support, per-interface timers, statistics, etc). Work can
+be done over time to increase the coverage to a more reasonable number.
+
+Translation Tables
+------------------
+
+Below is an example of a translator for the IETF family of models:
+
+.. code:: json
+
+ {
+ "frr-module-translator:frr-module-translator": {
+ "family": "ietf",
+ "module": [
+ {
+ "name": "ietf-interfaces@2018-01-09",
+ "deviations": "frr-deviations-ietf-interfaces",
+ "mappings": [
+ {
+ "custom": "/ietf-interfaces:interfaces/interface[name='KEY1']",
+ "native": "/frr-interface:lib/interface[name='KEY1'][vrf='default']"
+ },
+ {
+ "custom": "/ietf-interfaces:interfaces/interface[name='KEY1']/description",
+ "native": "/frr-interface:lib/interface[name='KEY1'][vrf='default']/description"
+ }
+ ]
+ },
+ {
+ "name": "ietf-routing@2018-01-25",
+ "deviations": "frr-deviations-ietf-routing",
+ "mappings": [
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']",
+ "native": "/frr-ripd:ripd/instance"
+ }
+ ]
+ },
+ {
+ "name": "ietf-rip@2018-02-03",
+ "deviations": "frr-deviations-ietf-rip",
+ "mappings": [
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/default-metric",
+ "native": "/frr-ripd:ripd/instance/default-metric"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/distance",
+ "native": "/frr-ripd:ripd/instance/distance/default"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/originate-default-route/enabled",
+ "native": "/frr-ripd:ripd/instance/default-information-originate"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/timers/update-interval",
+ "native": "/frr-ripd:ripd/instance/timers/update-interval"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/timers/holddown-interval",
+ "native": "/frr-ripd:ripd/instance/timers/holddown-interval"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/timers/flush-interval",
+ "native": "/frr-ripd:ripd/instance/timers/flush-interval"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/interfaces/interface[interface='KEY1']",
+ "native": "/frr-ripd:ripd/instance/interface[.='KEY1']"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/interfaces/interface[interface='KEY1']/split-horizon",
+ "native": "/frr-interface:lib/interface[name='KEY1'][vrf='default']/frr-ripd:rip/split-horizon"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/ipv4/neighbors/neighbor[ipv4-address='KEY1']",
+ "native": "/frr-ripd:ripd/state/neighbors/neighbor[address='KEY1']"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/ipv4/neighbors/neighbor[ipv4-address='KEY1']/last-update",
+ "native": "/frr-ripd:ripd/state/neighbors/neighbor[address='KEY1']/last-update"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/ipv4/neighbors/neighbor[ipv4-address='KEY1']/bad-packets-rcvd",
+ "native": "/frr-ripd:ripd/state/neighbors/neighbor[address='KEY1']/bad-packets-rcvd"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/ipv4/neighbors/neighbor[ipv4-address='KEY1']/bad-routes-rcvd",
+ "native": "/frr-ripd:ripd/state/neighbors/neighbor[address='KEY1']/bad-routes-rcvd"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/ipv4/routes/route[ipv4-prefix='KEY1']",
+ "native": "/frr-ripd:ripd/state/routes/route[prefix='KEY1']"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/ipv4/routes/route[ipv4-prefix='KEY1']/next-hop",
+ "native": "/frr-ripd:ripd/state/routes/route[prefix='KEY1']/next-hop"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/ipv4/routes/route[ipv4-prefix='KEY1']/interface",
+ "native": "/frr-ripd:ripd/state/routes/route[prefix='KEY1']/interface"
+ },
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/ipv4/routes/route[ipv4-prefix='KEY1']/metric",
+ "native": "/frr-ripd:ripd/state/routes/route[prefix='KEY1']/metric"
+ },
+ {
+ "custom": "/ietf-rip:clear-rip-route",
+ "native": "/frr-ripd:clear-rip-route"
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+The main motivation to use YANG itself to model YANG module translators
+was a practical one: leverage *libyang* to validate the structure of the
+user input (JSON files) instead of doing that manually in the
+*lib/yang_translator.c* file (tedious and error-prone work).
+
+Module translators can be loaded using the following CLI command:
+
+::
+
+ ripd(config)# yang module-translator load /usr/local/share/yang/ietf/frr-ietf-translator.json
+ % Module translator "ietf" loaded successfully.
+
+Module translators can also be loaded/unloaded programatically using the
+``yang_translator_load()/yang_translator_unload()`` functions within the
+northbound plugins. These functions are documented in the
+*lib/yang_translator.h* file.
+
+Each module translator must be assigned a “family” identifier
+(e.g. IETF, OpenConfig), and can contain mappings for multiple
+interrelated YANG modules. The mappings consist of pairs of
+custom/native XPath expressions that should be equivalent, despite
+belonging to different YANG modules.
+
+Example:
+
+.. code:: json
+
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/default-metric",
+ "native": "/frr-ripd:ripd/instance/default-metric"
+ },
+
+The nodes pointed by the custom and native XPaths must have compatible
+types. In the case of the example above, both nodes point to a YANG leaf
+of type ``uint8``, so the mapping is valid.
+
+In the example below, the “custom” XPath points to a YANG list
+(typeless), and the “native” XPath points to a YANG leaf-list of
+strings. In this exceptional case, the types are also considered to be
+compatible.
+
+.. code:: json
+
+ {
+ "custom": "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-rip:ripv2'][name='main']/ietf-rip:rip/interfaces/interface[interface='KEY1']",
+ "native": "/frr-ripd:ripd/instance/interface[.='KEY1']"
+ },
+
+The ``KEY1..KEY4`` values have a special meaning and are used to
+preserve the list keys while performing the XPath translation.
+
+Once a YANG module translator is loaded and validated at a syntactic
+level using *libyang*, further validations are performed to check for
+missing mappings (after loading the deviation modules) and incompatible
+YANG types. Example:
+
+::
+
+ ripd(config)# yang module-translator load /usr/local/share/yang/ietf/frr-ietf-translator.json
+ % Failed to load "/usr/local/share/yang/ietf/frr-ietf-translator.json"
+
+ Please check the logs for more details.
+
+::
+
+ 2018/09/03 15:18:45 RIP: yang_translator_validate_cb: YANG types are incompatible (xpath: "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/default-metric")
+ 2018/09/03 15:18:45 RIP: yang_translator_validate_cb: missing mapping for "/ietf-routing:routing/control-plane-protocols/control-plane-protocol/ietf-rip:rip/distance"
+ 2018/09/03 15:18:45 RIP: yang_translator_validate: failed to validate "ietf" module translator: 2 error(s)
+
+Overall, this translation mechanism based on XPath mappings is simple
+and functional, but only to a certain extent. The native models need to
+be reasonably similar to the models that are going be translated,
+otherwise the translation is compromised and a good coverage can’t be
+achieved. Other translation techniques must be investigated to address
+this shortcoming and make it possible to create more powerful YANG
+module translators.
+
+YANG module translators can be evaluated based on the following metrics:
+
+* Translation potential: is it possible to make complex translations, taking
+ several variables into account?
+
+* Complexity: measure of how easy or hard it is to write a module translator.
+
+* Speed: measure of how fast the translation can be achieved. Translation speed
+ is of fundamental importance, especially for operational data.
+
+* Robustness: can the translator be checked for inconsistencies at load time? A
+ module translator based on scripts wouldn’t fare well on this metric.
+
+* Round-trip conversions: can the translated data be translated back to the
+ original format without information loss?
+
+CLI Demonstration
+-----------------
+
+As of now the only northbound client that supports the YANG module
+translator is the FRR embedded CLI. The confd and sysrepo plugins need
+to be extended to support the module translator, which might be used not
+only for configuration data, but also for operational data, RPCs and
+notifications.
+
+In this demonstration, we’ll use the CLI ``configuration load`` command
+to load the following JSON configuration file specified using the IETF
+data hierarchy:
+
+.. code:: json
+
+ {
+ "ietf-interfaces:interfaces": {
+ "interface": [
+ {
+ "description": "Engineering",
+ "name": "eth0"
+ }
+ ]
+ },
+ "ietf-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "name": "main",
+ "type": "ietf-rip:ripv2",
+ "ietf-rip:rip": {
+ "default-metric": "2",
+ "distance": "80",
+ "interfaces": {
+ "interface": [
+ {
+ "interface": "eth0",
+ "split-horizon": "poison-reverse"
+ }
+ ]
+ },
+ "originate-default-route": {
+ "enabled": "true"
+ },
+ "timers": {
+ "flush-interval": "241",
+ "holddown-interval": "181",
+ "update-interval": "31"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+
+In order to load this configuration file, it’s necessary to load the
+IETF module translator first. Then, when entering the
+``configuration load`` command, the ``translate ietf`` parameters must
+be given to specify that the input needs to be translated using the
+previously loaded ``ietf`` module translator. Example:
+
+::
+
+ ripd(config)# configuration load file json /mnt/renato/git/frr/yang/example/ietf-rip.json
+ % Failed to load configuration:
+
+ Unknown element "interfaces".
+ ripd(config)#
+ ripd(config)# yang module-translator load /usr/local/share/yang/ietf/frr-ietf-translator.json
+ % Module translator "ietf" loaded successfully.
+
+ ripd(config)#
+ ripd(config)# configuration load file json translate ietf /mnt/renato/git/frr/yang/example/ietf-rip.json
+
+Now let’s check the candidate configuration to see if the configuration
+file was loaded successfully:
+
+::
+
+ ripd(config)# show configuration candidate
+ Configuration:
+ !
+ frr version 5.1-dev
+ frr defaults traditional
+ !
+ interface eth0
+ description Engineering
+ ip rip split-horizon poisoned-reverse
+ !
+ router rip
+ default-metric 2
+ distance 80
+ network eth0
+ default-information originate
+ timers basic 31 181 241
+ !
+ end
+ ripd(config)# show configuration candidate json
+ {
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth0",
+ "vrf": "default",
+ "description": "Engineering",
+ "frr-ripd:rip": {
+ "split-horizon": "poison-reverse"
+ }
+ }
+ ]
+ },
+ "frr-ripd:ripd": {
+ "instance": {
+ "default-metric": 2,
+ "distance": {
+ "default": 80
+ },
+ "interface": [
+ "eth0"
+ ],
+ "default-information-originate": true,
+ "timers": {
+ "flush-interval": 241,
+ "holddown-interval": 181,
+ "update-interval": 31
+ }
+ }
+ }
+ }
+
+As it can be seen, the candidate configuration is identical to the one
+defined in the *ietf-rip.json* file, only the structure is different.
+This means that the *ietf-rip.json* file was translated successfully.
+
+The ``ietf`` module translator can also be used to do the translation in
+other direction: transform data from the native format to the IETF
+format. This is shown below by altering the output of the
+``show configuration candidate json`` command using the
+``translate ietf`` parameter:
+
+::
+
+ ripd(config)# show configuration candidate json translate ietf
+ {
+ "ietf-interfaces:interfaces": {
+ "interface": [
+ {
+ "name": "eth0",
+ "description": "Engineering"
+ }
+ ]
+ },
+ "ietf-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "ietf-rip:ripv2",
+ "name": "main",
+ "ietf-rip:rip": {
+ "interfaces": {
+ "interface": [
+ {
+ "interface": "eth0",
+ "split-horizon": "poison-reverse"
+ }
+ ]
+ },
+ "default-metric": 2,
+ "distance": 80,
+ "originate-default-route": {
+ "enabled": true
+ },
+ "timers": {
+ "flush-interval": 241,
+ "holddown-interval": 181,
+ "update-interval": 31
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+
+As expected, this output is exactly identical to the configuration
+defined in the *ietf-rip.json* file. The module translator was able to
+do a round-trip conversion without information loss.
+
+Implementation Details
+----------------------
+
+A different libyang context is allocated for each YANG module
+translator. This is important to avoid collisions and ensure that
+non-native data can’t be instantiated in the running and candidate
+configurations.
diff --git a/doc/developer/northbound/yang-tools.rst b/doc/developer/northbound/yang-tools.rst
new file mode 100644
index 0000000000..fb5a287245
--- /dev/null
+++ b/doc/developer/northbound/yang-tools.rst
@@ -0,0 +1,114 @@
+Yang Tools
+==========
+
+Here's some information about various tools for working with yang
+models.
+
+yanglint cheat sheet
+~~~~~~~~~~~~~~~~~~~~
+
+ libyang project includes a feature-rich tool called yanglint(1) for
+ validation and conversion of the schemas and YANG modeled data. The
+ source codes are located at /tools/lint and can be used to explore
+ how an application is supposed to use the libyang library.
+ yanglint(1) binary as well as its man page are installed together
+ with the library itself.
+
+Validate a YANG module:
+
+.. code:: sh
+
+ $ yanglint -p <yang-search-path> module.yang
+
+Generate tree representation of a YANG module:
+
+.. code:: sh
+
+ $ yanglint -p <yang-search-path> -f tree module.yang
+
+Validate JSON/XML instance data:
+
+.. code:: sh
+
+ $ yanglint -p <yang-search-path> module.yang data.{json,xml}
+
+Convert JSON/XML instance data to another format:
+
+.. code:: sh
+
+ $ yanglint -p <yang-search-path> -f xml module.yang data.json
+ $ yanglint -p <yang-search-path> -f json module.yang data.xml
+
+*yanglint* also features an interactive mode which is very useful when
+needing to validate data from multiple modules at the same time. The
+*yanglint* README provides several examples:
+https://github.com/CESNET/libyang/blob/master/tools/lint/examples/README.md
+
+Man page (groff):
+https://github.com/CESNET/libyang/blob/master/tools/lint/yanglint.1
+
+pyang cheat sheet
+~~~~~~~~~~~~~~~~~
+
+ pyang is a YANG validator, transformator and code generator, written
+ in python. It can be used to validate YANG modules for correctness,
+ to transform YANG modules into other formats, and to generate code
+ from the modules.
+
+Obtaining and installing pyang:
+
+.. code:: sh
+
+ $ git clone https://github.com/mbj4668/pyang.git
+ $ cd pyang/
+ $ sudo python setup.py install
+
+Validate a YANG module:
+
+.. code:: sh
+
+ $ pyang --ietf -p <yang-search-path> module.yang
+
+Generate tree representation of a YANG module:
+
+.. code:: sh
+
+ $ pyang -f tree -p <yang-search-path> module.yang
+
+Indent a YANG file:
+
+.. code:: sh
+
+ $ pyang -p <yang-search-path> \
+ --keep-comments -f yang --yang-canonical \
+ module.yang -o module.yang
+
+Generate skeleton instance data:
+
+* XML:
+
+ .. code:: sh
+
+ $ pyang -p <yang-search-path> \
+ -f sample-xml-skeleton --sample-xml-skeleton-defaults \
+ module.yang [augmented-module1.yang ...] -o module.xml
+
+* JSON:
+
+ .. code:: sh
+
+ $ pyang -p <yang-search-path> \
+ -f jsonxsl module.yang -o module.xsl
+ $ xsltproc -o module.json module.xsl module.xml
+
+Validate XML instance data (works only with YANG 1.0):
+
+.. code:: sh
+
+ $ yang2dsdl -v module.xml module.yang
+
+vim
+~~~
+
+YANG syntax highlighting for vim:
+https://github.com/nathanalderson/yang.vim
diff --git a/doc/developer/ospf.rst b/doc/developer/ospf.rst
index a5164d552a..837a0bd185 100644
--- a/doc/developer/ospf.rst
+++ b/doc/developer/ospf.rst
@@ -9,4 +9,5 @@ OSPFD
ospf-api
ospf-sr
+ cspf
diff --git a/doc/developer/process-architecture.rst b/doc/developer/process-architecture.rst
index 33ef278c4d..85126cab30 100644
--- a/doc/developer/process-architecture.rst
+++ b/doc/developer/process-architecture.rst
@@ -46,7 +46,8 @@ implemented in FRR. This doc should be expanded and broken off into its own
section. For now it provides basic information necessary to understand the
interplay between the event system and kernel threads.
-The core event system is implemented in :file:`lib/thread.[ch]`. The primary
+The core event system is implemented in :file:`lib/event.c` and
+:file:`lib/frrevent.h`. The primary
structure is ``struct event_loop``, hereafter referred to as a
``threadmaster``. A ``threadmaster`` is a global state object, or context, that
holds all the tasks currently pending execution as well as statistics on tasks
@@ -57,41 +58,41 @@ execute. At initialization, a daemon will typically create one
fetch each task and execute it.
These tasks have various types corresponding to their general action. The types
-are given by integer macros in :file:`event.h` and are:
+are given by integer macros in :file:`frrevent.h` and are:
-``THREAD_READ``
+``EVENT_READ``
Task which waits for a file descriptor to become ready for reading and then
executes.
-``THREAD_WRITE``
+``EVENT_WRITE``
Task which waits for a file descriptor to become ready for writing and then
executes.
-``THREAD_TIMER``
+``EVENT_TIMER``
Task which executes after a certain amount of time has passed since it was
scheduled.
-``THREAD_EVENT``
+``EVENT_EVENT``
Generic task that executes with high priority and carries an arbitrary
integer indicating the event type to its handler. These are commonly used to
implement the finite state machines typically found in routing protocols.
-``THREAD_READY``
+``EVENT_READY``
Type used internally for tasks on the ready queue.
-``THREAD_UNUSED``
+``EVENT_UNUSED``
Type used internally for ``struct event`` objects that aren't being used.
The event system pools ``struct event`` to avoid heap allocations; this is
the type they have when they're in the pool.
-``THREAD_EXECUTE``
+``EVENT_EXECUTE``
Just before a task is run its type is changed to this. This is used to show
- ``X`` as the type in the output of :clicmd:`show thread cpu`.
+ ``X`` as the type in the output of :clicmd:`show event cpu`.
The programmer never has to work with these types explicitly. Each type of task
is created and queued via special-purpose functions (actually macros, but
irrelevant for the time being) for the specific type. For example, to add a
-``THREAD_READ`` task, you would call
+``EVENT_READ`` task, you would call
::
@@ -113,9 +114,9 @@ sockets needed for peerings or IPC.
To retrieve the next task to run the program calls ``event_fetch()``.
``event_fetch()`` internally computes which task to execute next based on
-rudimentary priority logic. Events (type ``THREAD_EVENT``) execute with the
+rudimentary priority logic. Events (type ``EVENT_EVENT``) execute with the
highest priority, followed by expired timers and finally I/O tasks (type
-``THREAD_READ`` and ``THREAD_WRITE``). When scheduling a task a function and an
+``EVENT_READ`` and ``EVENT_WRITE``). When scheduling a task a function and an
arbitrary argument are provided. The task returned from ``event_fetch()`` is
then executed with ``event_call()``.
@@ -135,23 +136,23 @@ Mapping the general names used in the figure to specific FRR functions:
- ``task`` is ``struct event *``
- ``fetch`` is ``event_fetch()``
-- ``exec()`` is ``event_call``
+- ``exec()`` is ``event_call()``
- ``cancel()`` is ``event_cancel()``
- ``schedule()`` is any of the various task-specific ``event_add_*`` functions
Adding tasks is done with various task-specific function-like macros. These
-macros wrap underlying functions in :file:`thread.c` to provide additional
+macros wrap underlying functions in :file:`event.c` to provide additional
information added at compile time, such as the line number the task was
scheduled from, that can be accessed at runtime for debugging, logging and
informational purposes. Each task type has its own specific scheduling function
-that follow the naming convention ``event_add_<type>``; see :file:`event.h`
+that follow the naming convention ``event_add_<type>``; see :file:`frrevent.h`
for details.
There are some gotchas to keep in mind:
- I/O tasks are keyed off the file descriptor associated with the I/O
operation. This means that for any given file descriptor, only one of each
- type of I/O task (``THREAD_READ`` and ``THREAD_WRITE``) can be scheduled. For
+ type of I/O task (``EVENT_READ`` and ``EVENT_WRITE``) can be scheduled. For
example, scheduling two write tasks one after the other will overwrite the
first task with the second, resulting in total loss of the first task and
difficult bugs.
@@ -209,7 +210,8 @@ Kernel Thread Wrapper
The basis for the integration of pthreads and the event system is a lightweight
wrapper for both systems implemented in :file:`lib/frr_pthread.[ch]`. The
header provides a core datastructure, ``struct frr_pthread``, that encapsulates
-structures from both POSIX threads and :file:`thread.[ch]`. In particular, this
+structures from both POSIX threads and :file:`event.c`, :file:`frrevent.h`.
+In particular, this
datastructure has a pointer to a ``threadmaster`` that runs within the pthread.
It also has fields for a name as well as start and stop functions that have
signatures similar to the POSIX arguments for ``pthread_create()``.
@@ -217,18 +219,18 @@ signatures similar to the POSIX arguments for ``pthread_create()``.
Calling ``frr_pthread_new()`` creates and registers a new ``frr_pthread``. The
returned structure has a pre-initialized ``threadmaster``, and its ``start``
and ``stop`` functions are initialized to defaults that will run a basic event
-loop with the given threadmaster. Calling ``frr_pthread_run`` starts the thread
+loop with the given threadmaster. Calling ``frr_pthread_run()`` starts the thread
with the ``start`` function. From there, the model is the same as the regular
event model. To schedule tasks on a particular pthread, simply use the regular
-:file:`thread.c` functions as usual and provide the ``threadmaster`` pointed to
+:file:`event.c` functions as usual and provide the ``threadmaster`` pointed to
from the ``frr_pthread``. As part of implementing the wrapper, the
-:file:`thread.c` functions were made thread-safe. Consequently, it is safe to
+:file:`event.c` functions were made thread-safe. Consequently, it is safe to
schedule events on a ``threadmaster`` belonging both to the calling thread as
well as *any other pthread*. This serves as the basis for inter-thread
communication and boils down to a slightly more complicated method of message
passing, where the messages are the regular task events as used in the
event-driven model. The only difference is thread cancellation, which requires
-calling ``event_cancel_async()`` instead of ``event_cancel`` to cancel a task
+calling ``event_cancel_async()`` instead of ``event_cancel()`` to cancel a task
currently scheduled on a ``threadmaster`` belonging to a different pthread.
This is necessary to avoid race conditions in the specific case where one
pthread wants to guarantee that a task on another pthread is cancelled before
@@ -236,16 +238,16 @@ proceeding.
In addition, the existing commands to show statistics and other information for
tasks within the event driven model have been expanded to handle multiple
-pthreads; running :clicmd:`show thread cpu` will display the usual event
+pthreads; running :clicmd:`show event cpu` will display the usual event
breakdown, but it will do so for each pthread running in the program. For
example, :ref:`bgpd` runs a dedicated I/O pthread and shows the following
-output for :clicmd:`show thread cpu`:
+output for :clicmd:`show event cpu`:
::
- frr# show thread cpu
+ frr# show event cpu
- Thread statistics for bgpd:
+ Event statistics for bgpd:
Showing statistics for pthread main
------------------------------------
diff --git a/doc/developer/rcu.rst b/doc/developer/rcu.rst
index 4fd56587ae..2335e8faed 100644
--- a/doc/developer/rcu.rst
+++ b/doc/developer/rcu.rst
@@ -232,6 +232,15 @@ Internals
that case, either all of the library's threads must be registered for RCU,
or the code must instead pass a (non-RCU) copy of the data to the library.
+.. c:function:: int frr_pthread_non_controlled_startup(pthread_t thread, const char *name, const char *os_name)
+
+ If a pthread is started outside the control of normal pthreads in frr
+ then frr_pthread_non_controlled_startup should be called. This will
+ properly setup both the pthread with rcu usage as well as some data
+ structures pertaining to the name of the pthread. This is especially
+ important if the pthread created ends up calling back into FRR and
+ one of the various zlog_XXX functions is called.
+
.. c:function:: void rcu_shutdown(void)
Stop the RCU sweeper thread and make sure all cleanup has finished.
diff --git a/doc/developer/requirements.txt b/doc/developer/requirements.txt
new file mode 100644
index 0000000000..483a4e9600
--- /dev/null
+++ b/doc/developer/requirements.txt
@@ -0,0 +1 @@
+sphinx_rtd_theme
diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst
index 202f0036f8..7a43314490 100644
--- a/doc/developer/scripting.rst
+++ b/doc/developer/scripting.rst
@@ -488,12 +488,6 @@ match *exactly*.
In the above example, we defined encoders/decoders for a value of
``struct prefix *``, but not ``struct prefix`` or ``const struct prefix *``.
-``const`` values are a special case. We want to use them in our Lua scripts
-but not modify them, so creating a decoder for them would be meaningless.
-But we still need a decoder for the type of value so that the compiler will be
-satisfied.
-For that, use ``lua_decode_noop``:
-
.. code-block:: diff
#define DECODE_ARGS_WITH_STATE(L, value) \
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index b4c752a473..652ee4e1af 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -5,13 +5,15 @@
dev_RSTFILES = \
doc/developer/bgp-typecodes.rst \
doc/developer/bgpd.rst \
+ doc/developer/bmp.rst \
doc/developer/building-frr-for-alpine.rst \
+ doc/developer/building-frr-for-archlinux.rst \
doc/developer/building-frr-for-centos6.rst \
doc/developer/building-frr-for-centos7.rst \
doc/developer/building-frr-for-debian8.rst \
doc/developer/building-frr-for-debian9.rst \
+ doc/developer/building-frr-for-debian12.rst \
doc/developer/building-frr-for-fedora.rst \
- doc/developer/building-frr-for-opensuse.rst \
doc/developer/building-frr-for-freebsd10.rst \
doc/developer/building-frr-for-freebsd11.rst \
doc/developer/building-frr-for-freebsd13.rst \
@@ -19,14 +21,17 @@ dev_RSTFILES = \
doc/developer/building-frr-for-netbsd6.rst \
doc/developer/building-frr-for-netbsd7.rst \
doc/developer/building-frr-for-openbsd6.rst \
+ doc/developer/building-frr-for-opensuse.rst \
doc/developer/building-frr-for-openwrt.rst \
doc/developer/building-frr-for-ubuntu1404.rst \
doc/developer/building-frr-for-ubuntu1604.rst \
doc/developer/building-frr-for-ubuntu1804.rst \
doc/developer/building-frr-for-ubuntu2004.rst \
+ doc/developer/building-frr-for-ubuntu2204.rst \
doc/developer/building-libunwind-note.rst \
doc/developer/building-libyang.rst \
doc/developer/building.rst \
+ doc/developer/checkpatch.rst \
doc/developer/cli.rst \
doc/developer/conf.py \
doc/developer/cross-compiling.rst \
@@ -64,6 +69,19 @@ dev_RSTFILES = \
doc/developer/workflow.rst \
doc/developer/xrefs.rst \
doc/developer/zebra.rst \
+ doc/developer/northbound/advanced-topics.rst \
+ doc/developer/northbound/architecture.rst \
+ doc/developer/northbound/demos.rst \
+ doc/developer/northbound/links.rst \
+ doc/developer/northbound/northbound.rst \
+ doc/developer/northbound/operational-data-rpcs-and-notifications.rst \
+ doc/developer/northbound/plugins-sysrepo.rst \
+ doc/developer/northbound/ppr-basic-test-topology.rst \
+ doc/developer/northbound/ppr-mpls-basic-test-topology.rst \
+ doc/developer/northbound/retrofitting-configuration-commands.rst \
+ doc/developer/northbound/transactional-cli.rst \
+ doc/developer/northbound/yang-module-translator.rst \
+ doc/developer/northbound/yang-tools.rst \
# end
EXTRA_DIST += \
diff --git a/doc/developer/topotests-markers.rst b/doc/developer/topotests-markers.rst
index 9f92412595..670bf0d255 100644
--- a/doc/developer/topotests-markers.rst
+++ b/doc/developer/topotests-markers.rst
@@ -12,6 +12,7 @@ systems, all tests must be marked with at least one of the following markers:
* eigrpd
* isisd
* ldpd
+* mgmtd
* nhrpd
* ospf6d
* ospfd
@@ -64,12 +65,12 @@ Adding a single marker:
import pytest
...
-
+
# add after imports, before defining classes or functions:
pytestmark = pytest.mark.bfdd
-
+
...
-
+
def test_using_bfdd():
@@ -79,16 +80,16 @@ Adding multiple markers:
import pytest
...
-
+
# add after imports, before defining classes or functions:
pytestmark = [
pytest.mark.bgpd,
pytest.mark.ospfd,
pytest.mark.ospf6d
]
-
+
...
-
+
def test_using_bgpd_ospfd_ospf6d():
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index 13936e18ed..b89b63029b 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -8,41 +8,52 @@ Topotests is a suite of topology tests for FRR built on top of micronet.
Installation and Setup
----------------------
-Topotests run under python3. Additionally, for ExaBGP (which is used
-in some of the BGP tests) an older python2 version (and the python2
-version of ``pip``) must be installed.
+Topotests run under python3.
-Tested with Ubuntu 20.04,Ubuntu 18.04, and Debian 11.
+Tested with Ubuntu 22.04,Ubuntu 20.04, and Debian 12.
-Instructions are the same for all setups (i.e. ExaBGP is only used for
-BGP tests).
+Python protobuf version < 4 is required b/c python protobuf >= 4 requires a
+protoc >= 3.19, and older package versions are shipped by in the above distros.
+
+Instructions are the same for all setups. However, ExaBGP is only used for
+BGP tests.
+
+Tshark is only required if you enable any packet captures on test runs.
+
+Valgrind is only required if you enable valgrind on test runs.
Installing Topotest Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code:: shell
- apt-get install gdb
- apt-get install iproute2
- apt-get install net-tools
- apt-get install python3-pip
+ apt-get install \
+ gdb \
+ iproute2 \
+ net-tools \
+ python3-pip \
+ iputils-ping \
+ tshark \
+ valgrind
python3 -m pip install wheel
- python3 -m pip install 'pytest>=6.2.4'
- python3 -m pip install 'pytest-xdist>=2.3.0'
+ python3 -m pip install 'pytest>=6.2.4' 'pytest-xdist>=2.3.0'
python3 -m pip install 'scapy>=2.4.5'
python3 -m pip install xmltodict
- # Use python2 pip to install older ExaBGP
- python2 -m pip install 'exabgp<4.0.0'
+ python3 -m pip install git+https://github.com/Exa-Networks/exabgp@0659057837cd6c6351579e9f0fa47e9fb7de7311
useradd -d /var/run/exabgp/ -s /bin/false exabgp
- # To enable the gRPC topotest install:
- python3 -m pip install grpcio grpcio-tools
+The version of protobuf package that is installed on your system will determine
+which versions of the python protobuf packages you need to install.
- # Install Socat tool to run PIMv6 tests,
- # Socat code can be taken from below url,
- # which has latest changes done for PIMv6,
- # join and traffic:
- https://github.com/opensourcerouting/socat/
+.. code:: shell
+ # - Either - For protobuf version <= 3.12
+ python3 -m pip install 'protobuf<4'
+
+ # - OR- for protobuf version >= 3.21
+ python3 -m pip install 'protobuf>=4'
+
+ # To enable the gRPC topotest also install:
+ python3 -m pip install grpcio grpcio-tools
Enable Coredumps
@@ -114,11 +125,12 @@ If you prefer to manually build FRR, then use the following suggested config:
./configure \
--prefix=/usr \
- --localstatedir=/var/run/frr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
--sbindir=/usr/lib/frr \
- --sysconfdir=/etc/frr \
--enable-vtysh \
--enable-pimd \
+ --enable-pim6d \
--enable-sharpd \
--enable-multipath=64 \
--enable-user=frr \
@@ -187,13 +199,15 @@ Analyze Test Results (``analyze.py``)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default router and execution logs are saved in ``/tmp/topotests`` and an XML
-results file is saved in ``/tmp/topotests.xml``. An analysis tool ``analyze.py``
-is provided to archive and analyze these results after the run completes.
+results file is saved in ``/tmp/topotests/topotests.xml``. An analysis tool
+``analyze.py`` is provided to archive and analyze these results after the run
+completes.
After the test run completes one should pick an archive directory to store the
results in and pass this value to ``analyze.py``. On first execution the results
-are copied to that directory from ``/tmp``, and subsequent runs use that
-directory for analyzing the results. Below is an example of this which also
+are moved to that directory from ``/tmp/topotests``. Subsequent runs of
+``analyze.py`` with the same args will use that directories contents for instead
+of copying any new results from ``/tmp``. Below is an example of this which also
shows the default behavior which is to display all failed and errored tests in
the run.
@@ -205,7 +219,7 @@ the run.
bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py::test_BGP_GR_10_p2
bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_routingTable
-Here we see that 4 tests have failed. We an dig deeper by displaying the
+Here we see that 4 tests have failed. We can dig deeper by displaying the
captured logs and errors. First let's redisplay the results enumerated by adding
the ``-E`` flag
@@ -225,8 +239,8 @@ the number of the test we are interested in along with ``--errmsg`` option.
~/frr/tests/topotests# ./analyze.py -Ar run-save -T0 --errmsg
bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_converge: AssertionError: BGP did not converge:
- IPv4 Unicast Summary (VIEW 1):
- BGP router identifier 172.30.1.1, local AS number 100 vrf-id -1
+ IPv4 Unicast Summary:
+ BGP router identifier 172.30.1.1, local AS number 100 VIEW 1 vrf-id -1
BGP table version 1
RIB entries 1, using 184 bytes of memory
Peers 3, using 2169 KiB of memory
@@ -240,9 +254,11 @@ the number of the test we are interested in along with ``--errmsg`` option.
assert False
-Now to look at the full text of the error for a failed test we use ``-T N``
-where N is the number of the test we are interested in along with ``--errtext``
-option.
+Now to look at the error text for a failed test we can use ``-T RANGES`` where
+``RANGES`` can be a number (e.g., ``5``), a range (e.g., ``0-10``), or a comma
+separated list numbers and ranges (e.g., ``5,10-20,30``) of the test cases we
+are interested in along with ``--errtext`` option. In the example below we'll
+select the first failed test case.
.. code:: shell
@@ -259,8 +275,8 @@ option.
> assert False, "BGP did not converge:\n%s" % bgpStatus
E AssertionError: BGP did not converge:
E
- E IPv4 Unicast Summary (VIEW 1):
- E BGP router identifier 172.30.1.1, local AS number 100 vrf-id -1
+ E IPv4 Unicast Summary:
+ E BGP router identifier 172.30.1.1, local AS number 100 VIEW 1 vrf-id -1
[...]
E Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
E 172.16.1.1 4 65001 0 0 0 0 0 never Connect 0 N/A
@@ -268,8 +284,8 @@ option.
[...]
To look at the full capture for a test including the stdout and stderr which
-includes full debug logs, just use the ``-T N`` option without the ``--errmsg``
-or ``--errtext`` options.
+includes full debug logs, use ``--full`` option, or specify a ``-T RANGES`` without
+specifying ``--errmsg`` or ``--errtext``.
.. code:: shell
@@ -289,6 +305,46 @@ or ``--errtext`` options.
--------------------------------- Captured Out ---------------------------------
system-err: --------------------------------- Captured Err ---------------------------------
+Filtered results
+""""""""""""""""
+
+There are 4 types of test results, [e]rrored, [f]ailed, [p]assed, and
+[s]kipped. One can select the set of results to show with the ``-S`` or
+``--select`` flags along with the letters for each type (i.e., ``-S efps``
+would select all results). By default ``analyze.py`` will use ``-S ef`` (i.e.,
+[e]rrors and [f]ailures) unless the ``--search`` filter is given in which case
+the default is to search all results (i.e., ``-S efps``).
+
+One can find all results which contain a ``REGEXP``. To filter results using a
+regular expression use the ``--search REGEXP`` option. In this case, by default,
+all result types will be searched for a match against the given ``REGEXP``. If a
+test result output contains a match it is selected into the set of results to show.
+
+An example of using ``--search`` would be to search all tests results for some
+log message, perhaps a warning or error.
+
+Using XML Results File from CI
+""""""""""""""""""""""""""""""
+
+``analyze.py`` actually only needs the ``topotests.xml`` file to run. This is
+very useful for analyzing a CI run failure where one only need download the
+``topotests.xml`` artifact from the run and then pass that to ``analyze.py``
+with the ``-r`` or ``--results`` option.
+
+For local runs if you wish to simply copy the ``topotests.xml`` file (leaving
+the log files where they are), you can pass the ``-a`` (or ``--save-xml``)
+instead of the ``-A`` (or ``-save``) options.
+
+Analyze Results from a Container Run
+""""""""""""""""""""""""""""""""""""
+
+``analyze.py`` can also be used with ``docker`` or ``podman`` containers.
+Everything works exactly as with a host run except that you specify the name of
+the container, or the container-id, using the `-C` or ``--container`` option.
+``analyze.py`` will then use the results inside that containers
+``/tmp/topotests`` directory. It will extract and save those results when you
+pass the ``-A`` or ``-a`` options just as withe host results.
+
Execute single test
^^^^^^^^^^^^^^^^^^^
@@ -311,32 +367,6 @@ Test will set exit code which can be used with ``git bisect``.
For the simulated topology, see the description in the python file.
-StdErr log from daemos after exit
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-To enable the reporting of any messages seen on StdErr after the daemons exit,
-the following env variable can be set::
-
- export TOPOTESTS_CHECK_STDERR=Yes
-
-(The value doesn't matter at this time. The check is whether the env
-variable exists or not.) There is no pass/fail on this reporting; the
-Output will be reported to the console.
-
-Collect Memory Leak Information
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-FRR processes can report unfreed memory allocations upon exit. To
-enable the reporting of memory leaks, define an environment variable
-``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.::
-
- export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
-
-This will enable the check and output to console and the writing of
-the information to files with the given prefix (followed by testname),
-ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
-of a memory leak.
-
Running Topotests with AddressSanitizer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -365,8 +395,9 @@ for ``master`` branch:
./bootstrap.sh
./configure \
--enable-address-sanitizer \
- --prefix=/usr/lib/frr --sysconfdir=/etc/frr \
- --localstatedir=/var/run/frr \
+ --prefix=/usr/lib/frr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
--sbindir=/usr/lib/frr --bindir=/usr/lib/frr \
--with-moduledir=/usr/lib/frr/modules \
--enable-multipath=0 --enable-rtadv \
@@ -422,7 +453,7 @@ as shown in the examples below.
For each capture a window is opened displaying a live summary of the captured
packets. Additionally, the entire packet stream is captured in a pcap file in
-the tests log directory e.g.,::
+the tests log directory e.g.,:
.. code:: console
@@ -432,7 +463,7 @@ the tests log directory e.g.,::
-rw------- 1 root root 45172 Apr 19 05:30 capture-r2-r2-eth0.pcap
-rw------- 1 root root 48412 Apr 19 05:30 capture-sw1.pcap
...
--
+
Viewing Live Daemon Logs
""""""""""""""""""""""""
@@ -449,10 +480,10 @@ One can live view daemon or the frr logs in separate windows using the
For each capture a window is opened displaying a live summary of the captured
packets. Additionally, the entire packet stream is captured in a pcap file in
-the tests log directory e.g.,::
+the tests log directory e.g.,
-When using a unified log file `frr.log` one substitutes `frr` for the daemon
-name in the ``--logd`` CLI option, e.g.,
+When using a unified log file ``frr.log`` one substitutes ``frr`` for the
+daemon name in the ``--logd`` CLI option, e.g.,
.. code:: shell
@@ -538,6 +569,8 @@ Here's an example of launching ``vtysh`` on routers ``rt1`` and ``rt2``.
sudo -E pytest --vtysh=rt1,rt2 all-protocol-startup
+.. _debug_with_gdb:
+
Debugging with GDB
""""""""""""""""""
@@ -562,20 +595,80 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router
--gdb-breakpoints=nb_config_diff \
all-protocol-startup
+Finally, for Emacs users, you can specify ``--gdb-use-emacs``. When specified
+the first router and daemon to be launched in gdb will be launched and run with
+Emacs gdb functionality by using `emacsclient --eval` commands. This provides an
+IDE debugging experience for Emacs users. This functionality works best when
+using password-less sudo.
+
+Reporting Memleaks with FRR Memory Statistics
+"""""""""""""""""""""""""""""""""""""""""""""
+
+FRR reports all allocated FRR memory objects on exit to standard error.
+Topotest can be run to report such output as errors in order to check for
+memleaks in FRR memory allocations. Specifying the CLI argument
+``--memleaks`` will enable reporting FRR-based memory allocations at exit as errors.
+
+.. code:: shell
+
+ sudo -E pytest --memleaks all-protocol-startup
+
+
+StdErr log from daemos after exit
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When running with ``--memleaks``, to enable the reporting of other,
+non-memory related, messages seen on StdErr after the daemons exit,
+the following env variable can be set::
+
+ export TOPOTESTS_CHECK_STDERR=Yes
+
+(The value doesn't matter at this time. The check is whether the env
+variable exists or not.) There is no pass/fail on this reporting; the
+Output will be reported to the console.
+
+Collect Memory Leak Information
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When running with ``--memleaks``, FRR processes report unfreed memory
+allocations upon exit. To enable also reporting of memory leaks to a specific
+location, define an environment variable ``TOPOTESTS_CHECK_MEMLEAK`` with the
+file prefix, i.e.:
+
+::
+
+ export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
+
+For tests that support the TOPOTESTS_CHECK_MEMLEAK environment variable, this
+will enable output to the information to files with the given prefix (followed
+by testname), e.g.,:
+file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
+of a memory leak.
+
Detecting Memleaks with Valgrind
""""""""""""""""""""""""""""""""
Topotest can automatically launch all daemons with ``valgrind`` to check for
-memleaks. This is enabled by specifying 1 or 2 CLI arguments.
-``--valgrind-memleaks`` will enable general memleak detection, and
-``--valgrind-extra`` enables extra functionality including generating a
-suppression file. The suppression file ``tools/valgrind.supp`` is used when
-memleak detection is enabled.
+memleaks. This is enabled by specifying 1 to 3 CLI arguments.
+``--valgrind-memleaks`` enables memleak detection. ``--valgrind-extra`` enables
+extra functionality including generating a suppression file. The suppression
+file ``tools/valgrind.supp`` is used when memleak detection is enabled. Finally,
+``--valgrind-leak-kinds=KINDS`` can be used to modify what types of links are
+reported. This corresponds to valgrind's ``--show-link-kinds`` arg. The value is
+either ``all`` or a comma-separated list of types:
+``definite,indirect,possible,reachable``. The default is ``definite,possible``.
.. code:: shell
sudo -E pytest --valgrind-memleaks all-protocol-startup
+.. note:: GDB can be used in conjection with valgrind.
+
+ When you enable ``--valgrind-memleaks`` and you also launch various daemons
+ under GDB (debug_with_gdb_) topotest will connect the two utilities using
+ ``--vgdb-error=0`` and attaching to a ``vgdb`` process. This is very
+ useful for debugging bugs with use of uninitialized errors, et al.
+
Collecting Performance Data using perf(1)
"""""""""""""""""""""""""""""""""""""""""
@@ -597,6 +690,66 @@ during the config_timing test.
To specify different arguments for ``perf record``, one can use the
``--perf-options`` this will replace the ``-g`` used by default.
+Running Daemons under RR Debug (``rr record``)
+""""""""""""""""""""""""""""""""""""""""""""""
+
+Topotest can automatically launch any daemon under ``rr(1)`` to collect
+execution state. The daemon is run in the foreground with ``rr record``.
+
+The execution state will be saved in the router specific directory
+(in a `rr` subdir that rr creates) under the test's run directoy.
+
+Here's an example of collecting ``rr`` execution state from ``mgmtd`` on router
+``r1`` during the ``config_timing`` test.
+
+.. code:: console
+
+ $ sudo -E pytest --rr-routers=r1 --rr-daemons=mgmtd config_timing
+ ...
+ $ find /tmp/topotests/ -name '*perf.data*'
+ /tmp/topotests/config_timing.test_config_timing/r1/perf.data
+
+To specify additional arguments for ``rr record``, one can use the
+``--rr-options``.
+
+.. _code_coverage:
+
+Code coverage
+"""""""""""""
+Code coverage reporting requires installation of the ``gcov`` and ``lcov``
+packages.
+
+Code coverage can automatically be gathered for any topotest run. To support
+this FRR must first be compiled with the ``--enable-gcov`` configure option.
+This will cause *.gnco files to be created during the build. When topotests are
+run the statistics are generated and stored in *.gcda files. Topotest
+infrastructure will gather these files, capture the information into a
+``coverage.info`` ``lcov`` file and also report the coverage summary.
+
+To enable code coverage support pass the ``--cov-topotest`` argument to pytest.
+If you build your FRR in a directory outside of the FRR source directory you
+will also need to pass the ``--cov-frr-build-dir`` argument specifying the build
+directory location.
+
+During the topotest run the *.gcda files are generated into a ``gcda``
+sub-directory of the top-level run directory (i.e., normally
+``/tmp/topotests/gcda``). These files will then be copied at the end of the
+topotest run into the FRR build directory where the ``gcov`` and ``lcov``
+utilities expect to find them. This is done to deal with the various different
+file ownership and permissions.
+
+At the end of the run ``lcov`` will be run to capture all of the coverage data
+into a ``coverage.info`` file. This file will be located in the top-level run
+directory (i.e., normally ``/tmp/topotests/coverage.info``).
+
+The ``coverage.info`` file can then be used to generate coverage reports or file
+markup (e.g., using the ``genhtml`` utility) or enable markup within your
+IDE/editor if supported (e.g., the emacs ``cov-mode`` package)
+
+NOTE: the *.gcda files in ``/tmp/topotests/gcda`` are cumulative so if you do
+not remove them they will aggregate data across multiple topotest runs.
+
+
.. _topotests_docker:
Running Tests with Docker
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index f11fff5dee..f720f6279e 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -166,29 +166,27 @@ as early as possible, i.e. the first 2-week window.
For reference, the expected release schedule according to the above is:
-+---------+------------+------------+------------+------------+------------+
-| Release | 2023-03-07 | 2023-07-04 | 2023-10-31 | 2024-02-27 | 2024-06-25 |
-+---------+------------+------------+------------+------------+------------+
-| RC | 2023-02-21 | 2023-06-20 | 2023-10-17 | 2024-02-13 | 2024-06-11 |
-+---------+------------+------------+------------+------------+------------+
-| dev/X.Y | 2023-02-07 | 2023-06-06 | 2023-10-03 | 2024-01-30 | 2024-05-28 |
-+---------+------------+------------+------------+------------+------------+
-| freeze | 2023-01-24 | 2023-05-23 | 2023-09-19 | 2024-01-16 | 2024-05-14 |
-+---------+------------+------------+------------+------------+------------+
++---------+------------+------------+------------+
+| Release | 2024-03-12 | 2024-07-02 | 2024-11-05 |
++---------+------------+------------+------------+
+| RC | 2024-02-27 | 2024-06-18 | 2024-10-22 |
++---------+------------+------------+------------+
+| dev/X.Y | 2024-02-13 | 2024-06-04 | 2024-10-08 |
++---------+------------+------------+------------+
+| freeze | 2024-01-30 | 2024-05-21 | 2024-09-24 |
++---------+------------+------------+------------+
Here is the hint on how to get the dates easily:
.. code-block:: console
- ~$ # Last freeze date was 2023-09-19
- ~$ date +%F --date='2023-09-19 +119 days' # Next freeze date
- 2024-01-16
- ~$ date +%F --date='2024-01-16 +14 days' # Next dev/X.Y date
- 2024-01-30
- ~$ date +%F --date='2024-01-30 +14 days' # Next RC date
- 2024-02-13
- ~$ date +%F --date='2024-02-13 +14 days' # Next Release date
- 2024-02-27
+ ~$ # Release date is 2023-11-07 (First Tuesday each March/July/November)
+ ~$ date +%F --date='2023-11-07 -42 days' # Next freeze date
+ 2023-09-26
+ ~$ date +%F --date='2023-11-07 -28 days' # Next dev/X.Y date
+ 2023-10-10
+ ~$ date +%F --date='2023-11-07 -14 days' # Next RC date
+ 2023-10-24
Each release is managed by one or more volunteer release managers from the FRR
community. These release managers are expected to handle the branch for a period
@@ -537,7 +535,8 @@ Programming Languages, Tools and Libraries
==========================================
The core of FRR is written in C (gcc or clang supported) and makes
-use of GNU compiler extensions. A few non-essential scripts are
+use of GNU compiler extensions. Additionally, the CLI generation
+tool, `clippy`, requires Python. A few other non-essential scripts are
implemented in Perl and Python. FRR requires the following tools
to build distribution packages: automake, autoconf, texinfo, libtool and
gawk and various libraries (i.e. libpam and libjson-c).
@@ -760,6 +759,13 @@ following requirements have achieved consensus:
tools can catch uninitialized value use that would otherwise be suppressed by
the (incorrect) zero initialization.
+- Usage of ``system()`` or other c library routines that cause signals to
+ possibly be ignored are not allowed. This includes the ``fork()`` and
+ ``execXX`` call patterns, which is actually what system() does underneath
+ the covers. This pattern causes the system shutdown to never work properly
+ as the SIGINT sent is never received. It is better to just prohibit code
+ that does this instead of having to debug shutdown issues again.
+
Other than these specific rules, coding practices from the Linux kernel as
well as CERT or MISRA C guidelines may provide useful input on safe C code.
However, these rules are not applied as-is; some of them expressly collide
@@ -811,6 +817,7 @@ The project provides multiple tools to allow you to correctly style your code
as painlessly as possible, primarily built around ``clang-format``.
clang-format
+
In the project root there is a :file:`.clang-format` configuration file
which can be used with the ``clang-format`` source formatter tool from the
LLVM project. Most of the time, this is the easiest and smartest tool to
@@ -867,14 +874,19 @@ clang-format
https://clang.llvm.org/docs/ClangFormat.html
checkpatch.sh
+checkpatch.pl
+
+ .. seealso:: :ref:`checkpatch`
+
In the Linux kernel source tree there is a Perl script used to check
- incoming patches for style errors. FRR uses an adapted version of this
- script for the same purpose. It can be found at
- :file:`tools/checkpatch.sh`. This script takes a git-formatted diff or
- patch file, applies it to a clean FRR tree, and inspects the result to catch
- potential style errors. Running this script on your patches before
- submission is highly recommended. The CI system runs this script as well and
- will comment on the PR with the results if style errors are found.
+ incoming patches for style errors. FRR uses a shell script front end and an
+ adapted version of the perl script for the same purpose. These scripts can
+ be found at :file:`tools/checkpatch.sh` and :file:`tools/checkpatch.pl`.
+ This script takes a git-formatted diff or patch file, applies it to a clean
+ FRR tree, and inspects the result to catch potential style errors. Running
+ this script on your patches before submission is highly recommended. The CI
+ system runs this script as well and will comment on the PR with the results
+ if style errors are found.
It is run like this::
@@ -915,6 +927,10 @@ checkpatch.sh
If the script finds one or more WARNINGs it will exit with 1. If it finds
one or more ERRORs it will exit with 2.
+ For convenience the Linux documentation for the :file:`tools/checkpatch.pl`
+ script has been included unmodified (i.e., it has not been updated to
+ reflect local changes) :doc:`here <checkpatch>`
+
Please remember that while FRR provides these tools for your convenience,
responsibility for properly formatting your code ultimately lies on the
@@ -1330,10 +1346,23 @@ frr-format plugin
Using the plugin also changes the string for ``PRI[udx]64`` from the
system value to ``%L[udx]`` (normally ``%ll[udx]`` or ``%l[udx]``.)
-Additionally, the FRR codebase is regularly scanned with Coverity.
-Unfortunately Coverity does not have the ability to handle scanning pull
-requests, but after code is merged it will send an email notifying project
-members with Coverity access of newly introduced defects.
+Additionally, the FRR codebase is regularly scanned for static analysis
+errors with Coverity and pull request changes are scanned as part of the
+Continuous Integration (CI) process. Developers can scan their commits for
+Coverity static analysis errors prior to submission using the
+``scan-build`` command. To use this command, the ``clang-tools`` package must
+be installed. For example, this can be accomplished on Ubuntu with the
+``sudo apt-get install clang-tools`` command. Then, touch the files you want scanned and
+invoke the ``scan-build`` command. For example::
+
+ cd ~/GitHub/frr
+ touch ospfd/ospf_flood.c ospfd/ospf_vty.c ospfd/ospf_opaque.c
+ cd build
+ scan-build make -j32
+
+The results of the scan including any static analysis errors will appear inline.
+Additionally, there will a directory in the /tmp containing the Coverity
+reports (e.g., scan-build-2023-06-09-120100-473730-1).
Executing non-installed dynamic binaries
----------------------------------------
diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst
index 5f039758a5..482df96267 100644
--- a/doc/developer/zebra.rst
+++ b/doc/developer/zebra.rst
@@ -159,229 +159,22 @@ Past Versions
Zebra Protocol Commands
-----------------------
-+------------------------------------+-------+
-| Command | Value |
-+====================================+=======+
-| ZEBRA_INTERFACE_ADD | 0 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_DELETE | 1 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_ADDRESS_ADD | 2 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_ADDRESS_DELETE | 3 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_UP | 4 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_DOWN | 5 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_SET_MASTER | 6 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_SET_PROTODOWN | 7 |
-+------------------------------------+-------+
-| ZEBRA_ROUTE_ADD | 8 |
-+------------------------------------+-------+
-| ZEBRA_ROUTE_DELETE | 9 |
-+------------------------------------+-------+
-| ZEBRA_ROUTE_NOTIFY_OWNER | 10 |
-+------------------------------------+-------+
-| ZEBRA_REDISTRIBUTE_ADD | 11 |
-+------------------------------------+-------+
-| ZEBRA_REDISTRIBUTE_DELETE | 12 |
-+------------------------------------+-------+
-| ZEBRA_REDISTRIBUTE_DEFAULT_ADD | 13 |
-+------------------------------------+-------+
-| ZEBRA_REDISTRIBUTE_DEFAULT_DELETE | 14 |
-+------------------------------------+-------+
-| ZEBRA_ROUTER_ID_ADD | 15 |
-+------------------------------------+-------+
-| ZEBRA_ROUTER_ID_DELETE | 16 |
-+------------------------------------+-------+
-| ZEBRA_ROUTER_ID_UPDATE | 17 |
-+------------------------------------+-------+
-| ZEBRA_HELLO | 18 |
-+------------------------------------+-------+
-| ZEBRA_CAPABILITIES | 19 |
-+------------------------------------+-------+
-| ZEBRA_NEXTHOP_REGISTER | 20 |
-+------------------------------------+-------+
-| ZEBRA_NEXTHOP_UNREGISTER | 21 |
-+------------------------------------+-------+
-| ZEBRA_NEXTHOP_UPDATE | 22 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_NBR_ADDRESS_ADD | 23 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_NBR_ADDRESS_DELETE | 24 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_BFD_DEST_UPDATE | 25 |
-+------------------------------------+-------+
-| ZEBRA_IMPORT_ROUTE_REGISTER | 26 |
-+------------------------------------+-------+
-| ZEBRA_IMPORT_ROUTE_UNREGISTER | 27 |
-+------------------------------------+-------+
-| ZEBRA_BFD_DEST_REGISTER | 29 |
-+------------------------------------+-------+
-| ZEBRA_BFD_DEST_DEREGISTER | 30 |
-+------------------------------------+-------+
-| ZEBRA_BFD_DEST_UPDATE | 31 |
-+------------------------------------+-------+
-| ZEBRA_BFD_DEST_REPLAY | 32 |
-+------------------------------------+-------+
-| ZEBRA_REDISTRIBUTE_ROUTE_ADD | 33 |
-+------------------------------------+-------+
-| ZEBRA_REDISTRIBUTE_ROUTE_DEL | 34 |
-+------------------------------------+-------+
-| ZEBRA_VRF_UNREGISTER | 35 |
-+------------------------------------+-------+
-| ZEBRA_VRF_ADD | 36 |
-+------------------------------------+-------+
-| ZEBRA_VRF_DELETE | 37 |
-+------------------------------------+-------+
-| ZEBRA_VRF_LABEL | 38 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_VRF_UPDATE | 39 |
-+------------------------------------+-------+
-| ZEBRA_BFD_CLIENT_REGISTER | 40 |
-+------------------------------------+-------+
-| ZEBRA_BFD_CLIENT_DEREGISTER | 41 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_ENABLE_RADV | 42 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_DISABLE_RADV | 43 |
-+------------------------------------+-------+
-| ZEBRA_NEXTHOP_LOOKUP_MRIB | 44 |
-+------------------------------------+-------+
-| ZEBRA_INTERFACE_LINK_PARAMS | 45 |
-+------------------------------------+-------+
-| ZEBRA_MPLS_LABELS_ADD | 46 |
-+------------------------------------+-------+
-| ZEBRA_MPLS_LABELS_DELETE | 47 |
-+------------------------------------+-------+
-| ZEBRA_MPLS_LABELS_REPLACE | 48 |
-+------------------------------------+-------+
-| ZEBRA_IPMR_ROUTE_STATS | 49 |
-+------------------------------------+-------+
-| ZEBRA_LABEL_MANAGER_CONNECT | 50 |
-+------------------------------------+-------+
-| ZEBRA_LABEL_MANAGER_CONNECT_ASYNC | 51 |
-+------------------------------------+-------+
-| ZEBRA_GET_LABEL_CHUNK | 52 |
-+------------------------------------+-------+
-| ZEBRA_RELEASE_LABEL_CHUNK | 53 |
-+------------------------------------+-------+
-| ZEBRA_FEC_REGISTER | 54 |
-+------------------------------------+-------+
-| ZEBRA_FEC_UNREGISTER | 55 |
-+------------------------------------+-------+
-| ZEBRA_FEC_UPDATE | 56 |
-+------------------------------------+-------+
-| ZEBRA_ADVERTISE_DEFAULT_GW | 57 |
-+------------------------------------+-------+
-| ZEBRA_ADVERTISE_SVI_MACIP | 58 |
-+------------------------------------+-------+
-| ZEBRA_ADVERTISE_SUBNET | 59 |
-+------------------------------------+-------+
-| ZEBRA_ADVERTISE_ALL_VNI | 60 |
-+------------------------------------+-------+
-| ZEBRA_LOCAL_ES_ADD | 61 |
-+------------------------------------+-------+
-| ZEBRA_LOCAL_ES_DEL | 62 |
-+------------------------------------+-------+
-| ZEBRA_VNI_ADD | 63 |
-+------------------------------------+-------+
-| ZEBRA_VNI_DEL | 64 |
-+------------------------------------+-------+
-| ZEBRA_L3VNI_ADD | 65 |
-+------------------------------------+-------+
-| ZEBRA_L3VNI_DEL | 66 |
-+------------------------------------+-------+
-| ZEBRA_REMOTE_VTEP_ADD | 67 |
-+------------------------------------+-------+
-| ZEBRA_REMOTE_VTEP_DEL | 68 |
-+------------------------------------+-------+
-| ZEBRA_MACIP_ADD | 69 |
-+------------------------------------+-------+
-| ZEBRA_MACIP_DEL | 70 |
-+------------------------------------+-------+
-| ZEBRA_IP_PREFIX_ROUTE_ADD | 71 |
-+------------------------------------+-------+
-| ZEBRA_IP_PREFIX_ROUTE_DEL | 72 |
-+------------------------------------+-------+
-| ZEBRA_REMOTE_MACIP_ADD | 73 |
-+------------------------------------+-------+
-| ZEBRA_REMOTE_MACIP_DEL | 74 |
-+------------------------------------+-------+
-| ZEBRA_DUPLICATE_ADDR_DETECTION | 75 |
-+------------------------------------+-------+
-| ZEBRA_PW_ADD | 76 |
-+------------------------------------+-------+
-| ZEBRA_PW_DELETE | 77 |
-+------------------------------------+-------+
-| ZEBRA_PW_SET | 78 |
-+------------------------------------+-------+
-| ZEBRA_PW_UNSET | 79 |
-+------------------------------------+-------+
-| ZEBRA_PW_STATUS_UPDATE | 80 |
-+------------------------------------+-------+
-| ZEBRA_RULE_ADD | 81 |
-+------------------------------------+-------+
-| ZEBRA_RULE_DELETE | 82 |
-+------------------------------------+-------+
-| ZEBRA_RULE_NOTIFY_OWNER | 83 |
-+------------------------------------+-------+
-| ZEBRA_TABLE_MANAGER_CONNECT | 84 |
-+------------------------------------+-------+
-| ZEBRA_GET_TABLE_CHUNK | 85 |
-+------------------------------------+-------+
-| ZEBRA_RELEASE_TABLE_CHUNK | 86 |
-+------------------------------------+-------+
-| ZEBRA_IPSET_CREATE | 87 |
-+------------------------------------+-------+
-| ZEBRA_IPSET_DESTROY | 88 |
-+------------------------------------+-------+
-| ZEBRA_IPSET_ENTRY_ADD | 89 |
-+------------------------------------+-------+
-| ZEBRA_IPSET_ENTRY_DELETE | 90 |
-+------------------------------------+-------+
-| ZEBRA_IPSET_NOTIFY_OWNER | 91 |
-+------------------------------------+-------+
-| ZEBRA_IPSET_ENTRY_NOTIFY_OWNER | 92 |
-+------------------------------------+-------+
-| ZEBRA_IPTABLE_ADD | 93 |
-+------------------------------------+-------+
-| ZEBRA_IPTABLE_DELETE | 94 |
-+------------------------------------+-------+
-| ZEBRA_IPTABLE_NOTIFY_OWNER | 95 |
-+------------------------------------+-------+
-| ZEBRA_VXLAN_FLOOD_CONTROL | 96 |
-+------------------------------------+-------+
-| ZEBRA_VXLAN_SG_ADD | 97 |
-+------------------------------------+-------+
-| ZEBRA_VXLAN_SG_DEL | 98 |
-+------------------------------------+-------+
-| ZEBRA_VXLAN_SG_REPLAY | 99 |
-+------------------------------------+-------+
-| ZEBRA_MLAG_PROCESS_UP | 100 |
-+------------------------------------+-------+
-| ZEBRA_MLAG_PROCESS_DOWN | 101 |
-+------------------------------------+-------+
-| ZEBRA_MLAG_CLIENT_REGISTER | 102 |
-+------------------------------------+-------+
-| ZEBRA_MLAG_CLIENT_UNREGISTER | 103 |
-+------------------------------------+-------+
-| ZEBRA_MLAG_FORWARD_MSG | 104 |
-+------------------------------------+-------+
-| ZEBRA_ERROR | 105 |
-+------------------------------------+-------+
-| ZEBRA_CLIENT_CAPABILITIES | 106 |
-+------------------------------------+-------+
-| ZEBRA_OPAQUE_MESSAGE | 107 |
-+------------------------------------+-------+
-| ZEBRA_OPAQUE_REGISTER | 108 |
-+------------------------------------+-------+
-| ZEBRA_OPAQUE_UNREGISTER | 109 |
-+------------------------------------+-------+
-| ZEBRA_NEIGH_DISCOVER | 110 |
-+------------------------------------+-------+
+The definitions of zebra protocol commands can be found at ``lib/zclient.h``.
+
+
+Zebra Dataplane
+===============
+
+The zebra dataplane subsystem provides a framework for FIB
+programming. Zebra uses the dataplane to program the local kernel as
+it makes changes to objects such as IP routes, MPLS LSPs, and
+interface IP addresses. The dataplane runs in its own pthread, in
+order to off-load work from the main zebra pthread.
+
+The zebra dataplane API is versioned; the version number must be
+updated along with API changes. Plugins can test the current version
+number and confirm that they are compatible with the current version.
+
Dataplane batching
==================