summaryrefslogtreecommitdiff
path: root/doc/developer
diff options
context:
space:
mode:
Diffstat (limited to 'doc/developer')
-rw-r--r--doc/developer/_static/overrides.css16
-rw-r--r--doc/developer/building-frr-for-alpine.rst2
-rw-r--r--doc/developer/building-frr-for-ubuntu2004.rst2
-rw-r--r--doc/developer/conf.py28
-rw-r--r--doc/developer/link-state.rst238
-rw-r--r--doc/developer/logging.rst305
-rw-r--r--doc/developer/topotests.rst89
-rw-r--r--doc/developer/tracing.rst2
-rw-r--r--doc/developer/workflow.rst12
9 files changed, 613 insertions, 81 deletions
diff --git a/doc/developer/_static/overrides.css b/doc/developer/_static/overrides.css
index 1d702bb6e9..302b8d6bd7 100644
--- a/doc/developer/_static/overrides.css
+++ b/doc/developer/_static/overrides.css
@@ -214,6 +214,22 @@ pre {
.highlight .na { color: var(--primary-2); }
.highlight .nv { color: var(--complement-0); }
+.rst-content code.frrfmtout {
+ background-color: var(--secondary-1-9);
+ border-color: var(--secondary-1-1);
+ font-size:100%;
+}
+.rst-content code.frrfmtout::before {
+ content: "⇒ \"";
+}
+.rst-content code.frrfmtout::after {
+ content: "\"";
+}
+.rst-content code.frrfmtout span {
+ color: var(--secondary-1-4);
+ font-size:100%;
+}
+
strong {
font-weight:500;
}
diff --git a/doc/developer/building-frr-for-alpine.rst b/doc/developer/building-frr-for-alpine.rst
index f88fc7bfdc..68e58c9d76 100644
--- a/doc/developer/building-frr-for-alpine.rst
+++ b/doc/developer/building-frr-for-alpine.rst
@@ -85,8 +85,6 @@ startup. To configure by hand:
docker exec -it frr /bin/sh
vi /etc/frr/daemons
- cp /etc/frr/zebra.conf.sample /etc/frr/zebra.conf
- vi /etc/frr/zebra.conf
/etc/init.d/frr start
Or, to configure the daemons using /etc/frr from a host volume, put the
diff --git a/doc/developer/building-frr-for-ubuntu2004.rst b/doc/developer/building-frr-for-ubuntu2004.rst
index ffc05a6841..58d72e2891 100644
--- a/doc/developer/building-frr-for-ubuntu2004.rst
+++ b/doc/developer/building-frr-for-ubuntu2004.rst
@@ -27,7 +27,7 @@ ubuntu apt repositories; in order to install it:
.. code-block:: shell
- curl https://bootstrap.pypa.io/2.7/get-pip.py --output get-pip.py
+ curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
sudo python2 ./get-pip.py
# And verify the installation
diff --git a/doc/developer/conf.py b/doc/developer/conf.py
index f4bb65ec79..20265f4aad 100644
--- a/doc/developer/conf.py
+++ b/doc/developer/conf.py
@@ -17,6 +17,8 @@ import os
import re
import pygments
from sphinx.highlighting import lexers
+from sphinx.util import logging
+logger = logging.getLogger(__name__)
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -362,11 +364,37 @@ texinfo_documents = [
with open("../extra/frrlexer.py", "rb") as lex:
frrlexerpy = lex.read()
+frrfmt_re = re.compile(r'^\s*%(?P<spec>[^\s]+)\s+\((?P<types>.*)\)\s*$')
+
+def parse_frrfmt(env, text, node):
+ from sphinx import addnodes
+
+ m = frrfmt_re.match(text)
+ if not m:
+ logger.warning('could not parse frrfmt:: %r' % (text), location=node)
+ node += addnodes.desc_name(text, text)
+ return text
+
+ spec, types = m.group('spec'), m.group('types')
+
+ node += addnodes.desc_sig_operator('%', '%')
+ node += addnodes.desc_name(spec + ' ', spec + ' ')
+ plist = addnodes.desc_parameterlist()
+ for typ in types.split(','):
+ typ = typ.strip()
+ plist += addnodes.desc_parameter(typ, typ)
+ node += plist
+ return '%' + spec
+
# custom extensions here
def setup(app):
# object type for FRR CLI commands, can be extended to document parent CLI
# node later on
app.add_object_type("clicmd", "clicmd")
+
+ # printfrr extensions
+ app.add_object_type("frrfmt", "frrfmt", parse_node=parse_frrfmt)
+
# css overrides for HTML theme
app.add_stylesheet("overrides.css")
# load Pygments lexer for FRR config syntax
diff --git a/doc/developer/link-state.rst b/doc/developer/link-state.rst
index f1fc52966b..1cbaf27ffe 100644
--- a/doc/developer/link-state.rst
+++ b/doc/developer/link-state.rst
@@ -81,26 +81,47 @@ corresponds to a Link State information conveyed by the routing protocol.
Functions
^^^^^^^^^
-A set of functions is provided to create, delete and compare Link State Node:
+A set of functions is provided to create, delete and compare Link State
+Node, Atribute and Prefix:
.. c:function:: struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr router_id, struct in6_addr router6_id)
-.. c:function:: voidls_node_del(struct ls_node *node)
-.. c:function:: int ls_node_same(struct ls_node *n1, struct ls_node *n2)
+.. c:function:: struct ls_attributes *ls_attributes_new(struct ls_node_id adv, struct in_addr local, struct in6_addr local6, uint32_t local_id)
+.. c:function:: struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p)
-and Link State Attributes:
+ Create respectively a new Link State Node, Attribute or Prefix.
+ Structure is dynamically allocated. Link State Node ID (adv) is mandatory
+ and:
-.. c:function:: struct ls_attributes *ls_attributes_new(struct ls_node_id adv, struct in_addr local, struct in6_addr local6, uint32_t local_id)
+ - at least one of IPv4 or IPv6 must be provided for the router ID
+ (router_id or router6_id) for Node
+ - at least one of local, local6 or local_id must be provided for Attribute
+ - prefix is mandatory for Link State Prefix.
+
+.. c:function:: void ls_node_del(struct ls_node *node)
.. c:function:: void ls_attributes_del(struct ls_attributes *attr)
+.. c:function:: void ls_prefix_del(struct ls_prefix *pref)
+
+ Remove, respectively Link State Node, Attributes or Prefix.
+ Data structure is freed.
+
+.. c:function:: void ls_attributes_srlg_del(struct ls_attributes *attr)
+
+ Remove SRLGs attribute if defined. Data structure is freed.
+
+.. c:function:: int ls_node_same(struct ls_node *n1, struct ls_node *n2)
.. c:function:: int ls_attributes_same(struct ls_attributes *a1, struct ls_attributes *a2)
+.. c:function:: int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix*p2)
+
+ Check, respectively if two Link State Nodes, Attributes or Prefix are equal.
+ Note that these routines have the same return value sense as '==' (which is
+ different from a comparison).
-The low level API doesn't provide any particular functions for the Link State
-Prefix structure as this latter is simpler to manipulate.
Link State TED
--------------
This is the high level API that provides functions to create, update, delete a
-Link State Database to from a Traffic Engineering Database (TED).
+Link State Database to build a Traffic Engineering Database (TED).
Data Structures
^^^^^^^^^^^^^^^
@@ -143,35 +164,143 @@ A unique Key is used to identify both Vertices and Edges within the Graph.
.. c:type:: struct ls_prefix
.. c:type:: struct ls_ted
+TED stores Vertex, Edge and Subnet elements with a RB Tree structure.
+The Vertex key corresponds to the Router ID for OSPF and ISO System ID for
+IS-IS. The Edge key corresponds to the IPv4 address, the lowest 64 bits of
+the IPv6 address or the combination of the local & remote ID of the interface.
+The Subnet key corresponds to the Prefix address (v4 or v6).
-Functions
-^^^^^^^^^
+An additional status for Vertex, Edge and Subnet allows to determine the state
+of the element in the TED: UNSET, NEW, UPDATE, DELETE, SYNC, ORPHAN. Normal
+state is SYNC. NEW, UPDATE and DELETE are temporary state when element is
+processed. UNSET is normally never used and ORPHAN serves to identify elements
+that must be remove when TED is cleaning.
+
+Vertex, Edges and Subnets management functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. c:function:: struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node)
+.. c:function:: struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes)
+.. c:function:: struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref)
+
+ Add, respectively new Vertex, Edge or Subnet to the Link State Datebase.
+ Vertex, Edge or Subnet are created from, respectively the Link State Node,
+ Attribute or Prefix structure. Data structure are dynamically allocated.
+
.. c:function:: struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
+.. c:function:: struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes)
+.. c:function:: struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref)
+
+ Update, respectively Vertex, Edge or Subnet with, respectively the Link
+ State Node, Attribute or Prefix. A new data structure is created if no one
+ corresponds to the Link State Node, Attribute or Prefix. If element already
+ exists in the TED, its associated Link State information is replaced by the
+ new one if there are different and the old associated Link State information
+ is deleted and memory freed.
+
.. c:function:: void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex)
+.. c:function:: void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex)
+.. c:function:: void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge)
+.. c:function:: void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge)
+.. c:function:: void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet)
+.. c:function:: void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet)
+
+ Delete, respectively Link State Vertex, Edge or Subnet. Data structure are
+ freed but not the associated Link State information with the simple `_del()`
+ form of the function while the `_del_all()` version freed also associated
+ Link State information. TED is not modified if Vertex, Edge or Subnet is
+ NULL or not found in the Data Base. Note that references between Vertices,
+ Edges and Subnets are removed first.
+
.. c:function:: struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key)
.. c:function:: struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, struct ls_node_id id)
-.. c:function:: int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
-.. c:function:: struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes)
-.. c:function:: struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes)
-.. c:function:: void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge)
+ Find Vertex in the TED by its unique key or its Link State Node ID.
+ Return Vertex if found, NULL otherwise.
+
.. c:function:: struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key)
.. c:function:: struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes);
.. c:function:: struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, struct ls_attributes *attributes);
-.. c:function:: struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref)
-.. c:function:: void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet)
+ Find Edge in the Link State Data Base by its key, source or distination
+ (local IPv4 or IPv6 address or local ID) informations of the Link State
+ Attributes. Return Edge if found, NULL otherwise.
+
.. c:function:: struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
+ Find Subnet in the Link State Data Base by its key, i.e. the associated
+ prefix. Return Subnet if found, NULL otherwise.
+
+.. c:function:: int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
+.. c:function:: int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2)
+.. c:function:: int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2)
+
+ Check, respectively if two Vertices, Edges or Subnets are equal.
+ Note that these routines has the same return value sense as '=='
+ (which is different from a comparison).
+
+
+TED management functions
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Some helpers functions have been also provided to ease TED management:
+
.. c:function:: struct ls_ted *ls_ted_new(const uint32_t key, char *name, uint32_t asn)
+
+ Create a new Link State Data Base. Key must be different from 0.
+ Name could be NULL and AS number equal to 0 if unknown.
+
.. c:function:: void ls_ted_del(struct ls_ted *ted)
+.. c:function:: void ls_ted_del_all(struct ls_ted *ted)
+
+ Delete existing Link State Data Base. Vertices, Edges, and Subnets are not
+ removed with ls_ted_del() function while they are with ls_ted_del_all().
+
.. c:function:: void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, struct ls_edge *edge)
+
+ Connect Source and Destination Vertices by given Edge. Only non NULL source
+ and destination vertices are connected.
+
.. c:function:: void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
.. c:function:: void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
+
+ Connect / Disconnect Link State Edge to the Link State Vertex which could be
+ a Source (source = true) or a Destination (source = false) Vertex.
+
.. c:function:: void ls_disconnect_edge(struct ls_edge *edge)
+ Disconnect Link State Edge from both Source and Destination Vertex.
+ Note that Edge is not removed but its status is marked as ORPHAN.
+
+.. c:function:: void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex, struct zclient *zclient)
+
+ Clean Vertex structure by removing all Edges and Subnets marked as ORPHAN
+ from this vertex. Corresponding Link State Update message is sent if zclient
+ parameter is not NULL. Note that associated Link State Attribute and Prefix
+ are also removed and memory freed.
+
+.. c:function:: void ls_ted_clean(struct ls_ted *ted)
+
+ Clean Link State Data Base by removing all Vertices, Edges and SubNets
+ marked as ORPHAN. Note that associated Link State Node, Attributes and
+ Prefix are removed too.
+
+.. c:function:: void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_edge(struct ls_edeg *edge, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_vertices(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_edges(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_subnets(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_ted(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose)
+
+ Respectively, show Vertex, Edge, Subnet provided as parameter, all Vertices,
+ all Edges, all Subnets and the whole TED if not specified. Output could be
+ more detailed with verbose parameter for VTY output. If both JSON and VTY
+ output are specified, JSON takes precedence over VTY.
+
+.. c:function:: void ls_dump_ted(struct ls_ted *ted)
+
+ Dump TED information to the current logging output.
Link State Messages
-------------------
@@ -198,8 +327,8 @@ Figure 1 below, illustrates the ZAPI Opaque message exchange between a
message sequences are as follows:
- First, both *Producer* and *Consumer* must register to their respective ZAPI
- Opaque Message. **Link State Sync** for the *Producer* in order to receive
- Database synchronisation request from a *Consumer*. **Link State Update** for
+ Opaque Message: **Link State Sync** for the *Producer* in order to receive
+ Database synchronisation request from a *Consumer*, **Link State Update** for
the *Consumer* in order to received any Link State update from a *Producer*.
These register messages are stored by Zebra to determine to which daemon it
should redistribute the ZAPI messages it receives.
@@ -245,22 +374,22 @@ message sequences are as follows:
| | Request LS Sync | v \
| Request LS Sync |<----------------------------| |
|<-----------------------------| | Synchronistation
- | LS DB Sync | | Phase
- |----------------------------->| LS DB Sync | |
+ | LS DB Update | | Phase
+ |----------------------------->| LS DB Update | |
| |---------------------------->| |
- | LS DB Sync (cont'd) | | |
- |----------------------------->| LS DB Sync (cont'd) | |
+ | LS DB Update (cont'd) | | |
+ |----------------------------->| LS DB Update (cont'd) | |
| . |---------------------------->| |
| . | . | |
| . | . | |
- | LS DB Sync (end) | . | |
- |----------------------------->| LS DB Sync (end) | |
+ | LS DB Update (end) | . | |
+ |----------------------------->| LS DB Update (end) | |
| |---------------------------->| |
| | | /
: : :
: : :
- | LS Update | | \
- |----------------------------->| LS Update | |
+ | LS DB Update | | \
+ |----------------------------->| LS DB Update | |
| |---------------------------->| Update Phase
| | | |
: : : /
@@ -269,7 +398,7 @@ message sequences are as follows:
| | Unregister LS Update | |
| |<----------------------------| Deregister Phase
| | | |
- | LS Update | | |
+ | LS DB Update | | |
|----------------------------->| | |
| | | /
| | |
@@ -305,10 +434,65 @@ Opaque Link State type at once.
Functions
^^^^^^^^^
+.. c:function:: int ls_register(struct zclient *zclient, bool server)
+.. c:function:: int ls_unregister(struct zclient *zclient, bool server)
+
+ Register / Unregister daemon to received ZAPI Link State Opaque messages.
+ Server must be set to true for *Producer* and to false for *Consumer*.
+
+.. c:function:: int ls_request_sync(struct zclient *zclient)
+
+ Request initial Synchronisation to collect the whole Link State Database.
+
.. c:function:: struct ls_message *ls_parse_msg(struct stream *s)
+
+ Parse Link State Message from stream. Used this function once receiving a
+ new ZAPI Opaque message of type Link State.
+
+.. c:function:: void ls_delete_msg(struct ls_message *msg)
+
+ Delete existing message. Data structure is freed.
+
.. c:function:: int ls_send_msg(struct zclient *zclient, struct ls_message *msg, struct zapi_opaque_reg_info *dst)
+
+ Send Link State Message as new ZAPI Opaque message of type Link State.
+ If destination is not NULL, message is sent as Unicast otherwise it is
+ broadcast to all registered daemon.
+
.. c:function:: struct ls_message *ls_vertex2msg(struct ls_message *msg, struct ls_vertex *vertex)
.. c:function:: struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge)
.. c:function:: struct ls_message *ls_subnet2msg(struct ls_message *msg, struct ls_subnet *subnet)
+
+ Create respectively a new Link State Message from a Link State Vertex, Edge
+ or Subnet. If Link State Message is NULL, a new data structure is
+ dynamically allocated. Note that the Vertex, Edge and Subnet status is used
+ to determine the corresponding Link State Message event: ADD, UPDATE,
+ DELETE, SYNC.
+
+.. c:function:: int ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg)
+.. c:function:: int ls_msg2edge(struct ls_ted *ted, struct ls_message *msg)
+.. c:function:: int ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg)
+
+ Convert Link State Message respectively in Vertex, Edge or Subnet and
+ update the Link State Database accordingly to the message event: SYNC, ADD,
+ UPDATE or DELETE.
+
+.. c:function:: struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg, bool delete)
+.. c:function:: struct ls_element *ls_stream2ted(struct ls_ted *ted, struct ls_message *msg, bool delete)
+
+ Convert Link State Message or Stream Buffer in a Link State element (Vertex,
+ Edge or Subnet) and update the Link State Database accordingly to the
+ message event: SYNC, ADD, UPDATE or DELETE. The function return the generic
+ structure ls_element that point to the Vertex, Edge or Subnet which has been
+ added, updated or synchronous in the database. Note that the delete boolean
+ parameter governs the action for the DELETE action: true, Link State Element
+ is removed from the database and NULL is return. If set to false, database
+ is not updated and the function sets the Link State Element status to
+ Delete and return the element for futur deletion by the calling function.
+
.. c:function:: int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct zapi_opaque_reg_info *dst)
+ Send all the content of the Link State Data Base to the given destination.
+ Link State content is sent is this order: Vertices, Edges then Subnet.
+ This function must be used when a daemon request a Link State Data Base
+ Synchronization.
diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst
index a35e60619c..b827afd6cc 100644
--- a/doc/developer/logging.rst
+++ b/doc/developer/logging.rst
@@ -1,5 +1,7 @@
.. _logging:
+.. highlight:: c
+
Logging
=======
@@ -52,46 +54,50 @@ are available:
if (ret != buf)
XFREE(MTYPE_FOO, ret);
-Extensions
-^^^^^^^^^^
-
-``printfrr()`` format strings can be extended with suffixes after `%p` or
-`%d`. The following extended format specifiers are available:
-
-+-----------+--------------------------+----------------------------------------------+
-| Specifier | Argument | Output |
-+===========+==========================+==============================================+
-| ``%Lu`` | ``uint64_t`` | ``12345`` |
-+-----------+--------------------------+----------------------------------------------+
-| ``%Ld`` | ``int64_t`` | ``-12345`` |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pI4`` | ``struct in_addr *`` | ``1.2.3.4`` |
-| | | |
-| | ``in_addr_t *`` | |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pI6`` | ``struct in6_addr *`` | ``fe80::1234`` |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pIA`` | ``struct ipaddr *`` | ``1.2.3.4`` |
-| | | |
-| | | ``fe80::1234`` |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pFX`` | ``struct prefix *`` | ``fe80::1234/64`` |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pSG4`` | ``struct prefix_sg *`` | ``(*,1.2.3.4)`` |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pRN`` | ``struct route_node *`` | ``192.168.1.0/24`` (dst-only node) |
-| | | |
-| | | ``2001:db8::/32 from fe80::/64`` (SADR node) |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pNHv`` | ``struct nexthop *`` | ``1.2.3.4, via eth0`` |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pNHs`` | ``struct nexthop *`` | ``1.2.3.4 if 15`` |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pFX`` | ``struct bgp_dest *`` | ``fe80::1234/64`` (available in BGP only) |
-+-----------+--------------------------+----------------------------------------------+
+.. c:function:: ssize_t bprintfrr(struct fbuf *fb, const char *fmt, ...)
+.. c:function:: ssize_t vbprintfrr(struct fbuf *fb, const char *fmt, va_list)
+
+ These are the "lowest level" functions, which the other variants listed
+ above use to implement their functionality on top. Mainly useful for
+ implementing printfrr extensions since those get a ``struct fbuf *`` to
+ write their output to.
+
+.. c:macro:: FMT_NSTD(expr)
+
+ This macro turns off/on format warnings as needed when non-ISO-C
+ compatible printfrr extensions are used (e.g. ``%.*p`` or ``%Ld``.)::
+
+ vty_out(vty, "standard compatible %pI4\n", &addr);
+ FMT_NSTD(vty_out(vty, "non-standard %-47.*pHX\n", (int)len, buf));
+
+ When the frr-format plugin is in use, this macro is a no-op since the
+ frr-format plugin supports all printfrr extensions. Since the FRR CI
+ includes a system with the plugin enabled, this means format errors will
+ not slip by undetected even with FMT_NSTD.
+
+.. note::
+ ``printfrr()`` does not support the ``%n`` format.
+
+AS-Safety
+^^^^^^^^^
+
+``printfrr()`` are AS-Safe under the following conditions:
+
+* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory)
+* floating point specifiers are not AS-Safe (system printf is used for these)
+* the positional ``%1$d`` syntax should not be used (8 arguments are supported
+ while AS-Safe)
+* extensions are only AS-Safe if their printer is AS-Safe
+
+printfrr Extensions
+-------------------
+
+``printfrr()`` format strings can be extended with suffixes after `%p` or `%d`.
Printf features like field lengths can be used normally with these extensions,
-e.g. ``%-15pI4`` works correctly.
+e.g. ``%-15pI4`` works correctly, **except if the extension consumes the
+width or precision**. Extensions that do so are listed below as ``%*pXX``
+rather than ``%pXX``.
The extension specifier after ``%p`` or ``%d`` is always an uppercase letter;
by means of established pattern uppercase letters and numbers form the type
@@ -99,10 +105,7 @@ identifier which may be followed by lowercase flags.
You can grep the FRR source for ``printfrr_ext_autoreg`` to see all extended
printers and what exactly they do. More printers are likely to be added as
-needed/useful, so the list above may become outdated.
-
-``%Ld`` is not an "extension" for printfrr; it's wired directly into the main
-printf logic.
+needed/useful, so the list here may be outdated.
.. note::
@@ -111,16 +114,218 @@ printf logic.
**not** available when calling ``snprintf`` directly. You need to call
``snprintfrr`` instead.
-AS-Safety
-^^^^^^^^^
+Networking data types
+^^^^^^^^^^^^^^^^^^^^^
-``printfrr()`` are AS-Safe under the following conditions:
+.. role:: frrfmtout(code)
-* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory)
-* floating point specifiers are not AS-Safe (system printf is used for these)
-* the positional ``%1$d`` syntax should not be used (8 arguments are supported
- while AS-Safe)
-* extensions are only AS-Safe if their printer is AS-Safe
+.. frrfmt:: %pI4 (struct in_addr *, in_addr_t *)
+
+ :frrfmtout:`1.2.3.4`
+
+.. frrfmt:: %pI6 (struct in6_addr *)
+
+ :frrfmtout:`fe80::1234`
+
+.. frrfmt:: %pEA (struct ethaddr *)
+
+ :frrfmtout:`01:23:45:67:89:ab`
+
+.. frrfmt:: %pIA (struct ipaddr *)
+
+ :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234`
+
+.. frrfmt:: %pFX (struct prefix *)
+
+ :frrfmtout:`1.2.3.0/24` / :frrfmtout:`fe80::1234/64`
+
+ This accepts the following types:
+
+ - :c:struct:`prefix`
+ - :c:struct:`prefix_ipv4`
+ - :c:struct:`prefix_ipv6`
+ - :c:struct:`prefix_eth`
+ - :c:struct:`prefix_evpn`
+ - :c:struct:`prefix_fs`
+
+ It does **not** accept the following types:
+
+ - :c:struct:`prefix_ls`
+ - :c:struct:`prefix_rd`
+ - :c:struct:`prefix_ptr`
+ - :c:struct:`prefix_sg` (use :frrfmt:`%pSG4`)
+ - :c:union:`prefixptr` (dereference to get :c:struct:`prefix`)
+ - :c:union:`prefixconstptr` (dereference to get :c:struct:`prefix`)
+
+.. frrfmt:: %pSG4 (struct prefix_sg *)
+
+ :frrfmtout:`(*,1.2.3.4)`
+
+ This is *(S,G)* output for use in pimd. (Note prefix_sg is not a prefix
+ "subclass" like the other prefix_* structs.)
+
+.. frrfmt:: %pSU (union sockunion *)
+
+ ``%pSU``: :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234`
+
+ ``%pSUs``: :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234%89`
+ (adds IPv6 scope ID as integer)
+
+ ``%pSUp``: :frrfmtout:`1.2.3.4:567` / :frrfmtout:`[fe80::1234]:567`
+ (adds port)
+
+ ``%pSUps``: :frrfmtout:`1.2.3.4:567` / :frrfmtout:`[fe80::1234%89]:567`
+ (adds port and scope ID)
+
+.. frrfmt:: %pRN (struct route_node *, struct bgp_node *, struct agg_node *)
+
+ :frrfmtout:`192.168.1.0/24` (dst-only node)
+
+ :frrfmtout:`2001:db8::/32 from fe80::/64` (SADR node)
+
+.. frrfmt:: %pNH (struct nexthop *)
+
+ ``%pNHvv``: :frrfmtout:`via 1.2.3.4, eth0` — verbose zebra format
+
+ ``%pNHv``: :frrfmtout:`1.2.3.4, via eth0` — slightly less verbose zebra format
+
+ ``%pNHs``: :frrfmtout:`1.2.3.4 if 15` — same as :c:func:`nexthop2str()`
+
+.. frrfmt:: %pBD (struct bgp_dest *)
+
+ :frrfmtout:`fe80::1234/64`
+
+ (only available in bgpd.)
+
+.. frrfmt:: %dPF (int)
+
+ :frrfmtout:`AF_INET`
+
+ Prints an `AF_*` / `PF_*` constant. ``PF`` is used here to avoid confusion
+ with `AFI` constants, even though the FRR codebase prefers `AF_INET` over
+ `PF_INET` & co.
+
+.. frrfmt:: %dSO (int)
+
+ :frrfmtout:`SOCK_STREAM`
+
+General utility formats
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. frrfmt:: %m (no argument)
+
+ :frrfmtout:`Permission denied`
+
+ Prints ``strerror(errno)``. Does **not** consume any input argument, don't
+ pass ``errno``!
+
+ (This is a GNU extension not specific to FRR. FRR guarantees it is
+ available on all systems in printfrr, though BSDs support it in printf too.)
+
+.. frrfmt:: %pSQ (char *)
+
+ ([S]tring [Q]uote.) Like ``%s``, but produce a quoted string. Options:
+
+ ``n`` - treat ``NULL`` as empty string instead.
+
+ ``q`` - include ``""`` quotation marks. Note: ``NULL`` is printed as
+ ``(null)``, not ``"(null)"`` unless ``n`` is used too. This is
+ intentional.
+
+ ``s`` - use escaping suitable for RFC5424 syslog. This means ``]`` is
+ escaped too.
+
+ If a length is specified (``%*pSQ`` or ``%.*pSQ``), null bytes in the input
+ string do not end the string and are just printed as ``\x00``.
+
+.. frrfmt:: %pSE (char *)
+
+ ([S]tring [E]scape.) Like ``%s``, but escape special characters.
+ Options:
+
+ ``n`` - treat ``NULL`` as empty string instead.
+
+ Unlike :frrfmt:`%pSQ`, this escapes many more characters that are fine for
+ a quoted string but not on their own.
+
+ If a length is specified (``%*pSE`` or ``%.*pSE``), null bytes in the input
+ string do not end the string and are just printed as ``\x00``.
+
+.. frrfmt:: %pVA (struct va_format *)
+
+ Recursively invoke printfrr, with arguments passed in through:
+
+ .. c:struct:: va_format
+
+ .. c:member:: const char *fmt
+
+ Format string to use for the recursive printfrr call.
+
+ .. c:member:: va_list *va
+
+ Formatting arguments. Note this is passed as a pointer, not - as in
+ most other places - a direct struct reference. Internally uses
+ ``va_copy()`` so repeated calls can be made (e.g. for determining
+ output length.)
+
+.. frrfmt:: %pFB (struct fbuf *)
+
+ Insert text from a ``struct fbuf *``, i.e. the output of a call to
+ :c:func:`bprintfrr()`.
+
+.. frrfmt:: %*pHX (void *, char *, unsigned char *)
+
+ ``%pHX``: :frrfmtout:`12 34 56 78`
+
+ ``%pHXc``: :frrfmtout:`12:34:56:78` (separate with [c]olon)
+
+ ``%pHXn``: :frrfmtout:`12345678` (separate with [n]othing)
+
+ Insert hexdump. This specifier requires a precision or width to be
+ specified. A precision (``%.*pHX``) takes precedence, but generates a
+ compiler warning since precisions are undefined for ``%p`` in ISO C. If
+ no precision is given, the width is used instead (and normal handling of
+ the width is suppressed).
+
+ Note that width and precision are ``int`` arguments, not ``size_t``. Use
+ like::
+
+ char *buf;
+ size_t len;
+
+ snprintfrr(out, sizeof(out), "... %*pHX ...", (int)len, buf);
+
+ /* with padding to width - would generate a warning due to %.*p */
+ FMT_NSTD(snprintfrr(out, sizeof(out), "... %-47.*pHX ...", (int)len, buf));
+
+.. frrfmt:: %*pHS (void *, char *, unsigned char *)
+
+ ``%pHS``: :frrfmtout:`hex.dump`
+
+ This is a complementary format for :frrfmt:`%*pHX` to print the text
+ representation for a hexdump. Non-printable characters are replaced with
+ a dot.
+
+Integer formats
+^^^^^^^^^^^^^^^
+
+.. note::
+
+ These formats currently only exist for advanced type checking with the
+ ``frr-format`` GCC plugin. They should not be used directly since they will
+ 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>`_.
+
+.. frrfmt:: %Lu (uint64_t)
+
+ :frrfmtout:`12345`
+
+.. frrfmt:: %Ld (int64_t)
+
+ :frrfmtout:`-12345`
Log levels
----------
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index 7976a206f7..a86566dbb0 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -60,6 +60,7 @@ following steps will get you there on Ubuntu 20.04.
.. code:: shell
+ apt install libsnmp-dev
apt install snmpd snmp
apt install snmp-mibs-downloader
download-mibs
@@ -232,6 +233,85 @@ for ``master`` branch:
and create ``frr`` user and ``frrvty`` group as shown above.
+Debugging Topotest Failures
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For the below debugging options which launch programs, if the topotest is run
+within screen_ or tmux_, ``gdb``, the shell or ``vtysh`` will be launched using
+that windowing program, otherwise mininet's ``xterm`` functionality will be used
+to launch the given program.
+
+If you wish to force the use of ``xterm`` rather than ``tmux`` or ``screen``, or
+wish to use ``gnome-terminal`` instead of ``xterm``, set the environment
+variable ``FRR_TOPO_TERMINAL`` to either ``xterm`` or ``gnome-terminal``.
+
+.. _screen: https://www.gnu.org/software/screen/
+.. _tmux: https://github.com/tmux/tmux/wiki
+
+Spawning ``vtysh`` or Shells on Routers
+"""""""""""""""""""""""""""""""""""""""
+
+Topotest can automatically launch a shell or ``vtysh`` for any or all routers in
+a test. This is enabled by specifying 1 of 2 CLI arguments ``--shell`` or
+``--vtysh``. Both of these options can be set to a single router value, multiple
+comma-seperated values, or ``all``.
+
+When either of these options are specified topotest will pause after each test
+to allow for inspection of the router state.
+
+Here's an example of launching ``vtysh`` on routers ``rt1`` and ``rt2``.
+
+.. code:: shell
+
+ pytest --vtysh=rt1,rt2 all-protocol-startup
+
+Spawning Mininet CLI, ``vtysh`` or Shells on Routers on Test Failure
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+Similar to the previous section one can have ``vtysh`` or a shell launched on
+routers, but in this case only when a test fails. To launch the given process on
+each router after a test failure specify one of ``--shell-on-error`` or
+``--vtysh-on-error``.
+
+
+Here's an example of having ``vtysh`` launched on test failure.
+
+.. code:: shell
+
+ pytest --vtysh-on-error all-protocol-startup
+
+
+Additionally, one can have the mininet CLI invoked on test failures by
+specifying the ``--mininet-on-error`` CLI option as shown in the example below.
+
+.. code:: shell
+
+ pytest --mininet-on-error all-protocol-startup
+
+Debugging with GDB
+""""""""""""""""""
+
+Topotest can automatically launch any daemon with ``gdb``, possibly setting
+breakpoints for any test run. This is enabled by specifying 1 or 2 CLI arguments
+``--gdb-routers`` and ``--gdb-daemons``. Additionally ``--gdb-breakpoints`` can
+be used to automatically set breakpoints in the launched ``gdb`` processes.
+
+Each of these options can be set to a single value, multiple comma-seperated
+values, or ``all``. If ``--gdb-routers`` is empty but ``--gdb_daemons`` is set
+then the given daemons will be launched in ``gdb`` on all routers in the test.
+Likewise if ``--gdb_routers`` is set, but ``--gdb_daemons`` is empty then all
+daemons on the given routers will be launched in ``gdb``.
+
+Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router
+``r1`` with a breakpoint set on ``nb_config_diff``
+
+.. code:: shell
+
+ pytest --gdb-routers=r1 \
+ --gdb-daemons=bgpd,zebra \
+ --gdb-breakpoints=nb_config_diff \
+ all-protocol-startup
+
.. _topotests_docker:
Running Tests with Docker
@@ -400,6 +480,15 @@ Some things to keep in mind:
- Avoid including unstable data in your test: don't rely on link-local
addresses or ifindex values, for example, because these can change
from run to run.
+- Using sleep is almost never appropriate to wait for some convergence
+ event as the sole item done. As an example: if the test resets the peers
+ in BGP, the test should look for the peers reconverging instead of just
+ sleeping an arbitrary amount of time and continuing on. It is ok to
+ use sleep in a tight loop with appropriate show commands to ensure that
+ the protocol reaches the desired state. This should be bounded by
+ appropriate timeouts for the protocol in question though. See
+ verify_bgp_convergence as a good example of this. If you are having
+ troubles figuring out what to look for, please do not be afraid to ask.
Topotest File Hierarchy
diff --git a/doc/developer/tracing.rst b/doc/developer/tracing.rst
index ae4d621a8e..63b04585f1 100644
--- a/doc/developer/tracing.rst
+++ b/doc/developer/tracing.rst
@@ -396,7 +396,7 @@ modifying ``frr.service`` like so:
--- a/frr.service
+++ b/frr.service
@@ -7,6 +7,7 @@ Before=network.target
- OnFailure=heartbeat-failed@%n.service
+ OnFailure=heartbeat-failed@%n
[Service]
+Environment="LD_PRELOAD=liblttng-ust-fork.so"
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index abdbea5a9c..b4ddec10c9 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -500,10 +500,22 @@ made. For example, a change in :file:`bgpd/rfapi` would be formatted as::
The first line should be no longer than 50 characters. Subsequent lines should
be wrapped to 72 characters.
+The purpose of commit messages is to briefly summarize what the commit is
+changing. Therefore, the extended summary portion should be in the form of an
+English paragraph. Brief examples of program output are acceptable but if
+present should be short (on the order of 10 lines) and clearly demonstrate what
+has changed. The goal should be that someone with only passing familiarity with
+the code in question can understand what is being changed.
+
+Commit messages consisting entirely of program output are *unacceptable*. These
+do not describe the behavior changed. For example, putting VTYSH output or the
+result of test runs as the sole content of commit messages is unacceptable.
+
You must also sign off on your commit.
.. seealso:: :ref:`signing-off`
+
Source File Header
------------------