diff options
Diffstat (limited to 'doc/developer')
| -rw-r--r-- | doc/developer/library.rst | 3 | ||||
| -rw-r--r-- | doc/developer/lists.rst | 594 | ||||
| -rw-r--r-- | doc/developer/ospf-api.rst | 334 | ||||
| -rw-r--r-- | doc/developer/subdir.am | 1 |
4 files changed, 762 insertions, 170 deletions
diff --git a/doc/developer/library.rst b/doc/developer/library.rst index 77b2f229b7..4ba0c0ebc6 100644 --- a/doc/developer/library.rst +++ b/doc/developer/library.rst @@ -7,8 +7,9 @@ Library Facilities (libfrr) .. toctree:: :maxdepth: 2 - logging memtypes + lists + logging hooks cli modules diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst new file mode 100644 index 0000000000..6d60420b2f --- /dev/null +++ b/doc/developer/lists.rst @@ -0,0 +1,594 @@ +List implementations +==================== + +.. note:: + + The term *list* is used generically for lists, skiplists, trees and hash + tables in this document. + +Common list interface +--------------------- + +FRR includes a set of list-like data structure implementations with abstracted +common APIs. The purpose of this is easily allow swapping out one +data structure for another while also making the code easier to read and write. +There is one API for unsorted lists and a similar but not identical API for +sorted lists. + +For unsorted lists, the following implementations exist: + +- single-linked list with tail pointer (e.g. STAILQ in BSD) + +- atomic single-linked list with tail pointer + + +For sorted lists, these data structures are implemented: + +- single-linked list + +- atomic single-linked list + +- skiplist + +- red-black tree (based on OpenBSD RB_TREE) + +- hash table (note below) + +Except for hash tables, each of the sorted data structures has a variant with +unique and non-unique list items. Hash tables always require unique items +and mostly follow the "sorted" API but use the hash value as sorting +key. Also, iterating while modifying does not work with hash tables. + + +The following sorted structures are likely to be implemented at some point +in the future: + +- atomic skiplist + +- atomic hash table (note below) + + +The APIs are all designed to be as type-safe as possible. This means that +there will be a compiler warning when an item doesn't match the list, or +the return value has a different type, or other similar situations. **You +should never use casts with these APIs.** If a cast is neccessary in relation +to these APIs, there is probably something wrong with the overall design. + +Only the following pieces use dynamically allocated memory: + +- the hash table itself is dynamically grown and shrunk + +- skiplists store up to 4 next pointers inline but will dynamically allocate + memory to hold an item's 5th up to 16th next pointer (if they exist) + +Cheat sheet +----------- + +Available types: + +:: + + DECLARE_LIST + DECLARE_ATOMLIST + + DECLARE_SORTLIST_UNIQ + DECLARE_SORTLIST_NONUNIQ + DECLARE_ATOMLIST_UNIQ + DECLARE_ATOMLIST_NONUNIQ + DECLARE_SKIPLIST_UNIQ + DECLARE_SKIPLIST_NONUNIQ + DECLARE_RBTREE_UNIQ + DECLARE_RBTREE_NONUNIQ + + DECLARE_HASH + +Functions provided: + ++------------------------------------+------+------+---------+------------+ +| Function | LIST | HASH | \*_UNIQ | \*_NONUNIQ | ++====================================+======+======+=========+============+ +| _init, _fini | yes | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ +| _first, _next, _next_safe | yes | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ +| _add_head, _add_tail, _add_after | yes | -- | -- | -- | ++------------------------------------+------+------+---------+------------+ +| _add | -- | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ +| _del, _pop | yes | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ +| _find | -- | yes | yes | -- | ++------------------------------------+------+------+---------+------------+ +| _find_lt, _find_gteq | -- | -- | yes | yes | ++------------------------------------+------+------+---------+------------+ +| use with for_each() macros | yes | yes | yes | yes | ++------------------------------------+------+------+---------+------------+ + + +Datastructure type setup +------------------------ + +Each of the data structures has a ``PREDECL_*`` and a ``DECLARE_*`` macro to +set up an "instantiation" of the list. This works somewhat similar to C++ +templating, though much simpler. + +**In all following text, the Z prefix is replaced with a name choosen +for the instance of the datastructure.** + +The common setup pattern will look like this: + +.. code-block:: c + + PREDECL_XXX(Z) + struct item { + int otherdata; + struct Z_item mylistitem; + } + + struct Z_head mylisthead; + + /* unsorted: */ + DECLARE_XXX(Z, struct item, mylistitem) + + /* sorted, items that compare as equal cannot be added to list */ + int compare_func(const struct item *a, const struct item *b); + DECLARE_XXX_UNIQ(Z, struct item, mylistitem, compare_func) + + /* sorted, items that compare as equal can be added to list */ + int compare_func(const struct item *a, const struct item *b); + DECLARE_XXX_NONUNIQ(Z, struct item, mylistitem, compare_func) + + /* hash tables: */ + int compare_func(const struct item *a, const struct item *b); + uint32_t hash_func(const struct item *a); + DECLARE_XXX(Z, struct item, mylistitem, compare_func, hash_func) + +``XXX`` is replaced with the name of the data structure, e.g. ``SKIPLIST`` +or ``ATOMLIST``. The ``DECLARE_XXX`` invocation can either occur in a `.h` +file (if the list needs to be accessed from several C files) or it can be +placed in a `.c` file (if the list is only accessed from that file.) The +``PREDECL_XXX`` invocation defines the ``struct Z_item`` and ``struct +Z_head`` types and must therefore occur before these are used. + +To switch between compatible data structures, only these two lines need to be +changes. To switch to a data structure with a different API, some source +changes are necessary. + +Common iteration macros +----------------------- + +The following iteration macros work across all data structures: + +.. c:function:: for_each(Z, head, item) + + Equivalent to: + + .. code-block:: c + + for (item = Z_first(head); item; item = Z_next(head, item)) + + Note that this will fail if the list is modified while being iterated + over. + +.. c:function:: for_each_safe(Z, head, item) + + Same as the previous, but the next element is pre-loaded into a "hidden" + variable (named ``Z_safe``.) Equivalent to: + + .. code-block:: c + + for (item = Z_first(head); item; item = next) { + next = Z_next_safe(head, item); + ... + } + + .. warning:: + + Iterating over hash tables while adding or removing items is not + possible. The iteration position will be corrupted when the hash + tables is resized while iterating. This will cause items to be + skipped or iterated over twice. + +.. c:function:: for_each_from(Z, head, item, from) + + Iterates over the list, starting at item ``from``. This variant is "safe" + as in the previous macro. Equivalent to: + + .. code-block:: c + + for (item = from; item; item = from) { + from = Z_next_safe(head, item); + ... + } + + .. note:: + + The ``from`` variable is written to. This is intentional - you can + resume iteration after breaking out of the loop by keeping the ``from`` + value persistent and reusing it for the next loop. + +Common API +---------- + +The following documentation assumes that a list has been defined using +``Z`` as the name, and ``itemtype`` being the type of the list items (e.g. +``struct item``.) + +.. c:function:: void Z_init(struct Z_head *) + + Initializes the list for use. For most implementations, this just sets + some values. Hash tables are the only implementation that allocates + memory in this call. + +.. c:function:: void Z_fini(struct Z_head *) + + Reverse the effects of :c:func:`Z_init()`. The list must be empty + when this function is called. + + .. warning:: + + This function may ``assert()`` if the list is not empty. + +.. c:function:: size_t Z_count(struct Z_head *) + + Returns the number of items in a structure. All structures store a + counter in their `Z_head` so that calling this function completes + in O(1). + + .. note:: + + For atomic lists with concurrent access, the value will already be + outdated by the time this function returns and can therefore only be + used as an estimate. + +.. c:function:: itemtype *Z_first(struct Z_head *) + + Returns the first item in the structure, or ``NULL`` if the structure is + empty. This is O(1) for all data structures except red-black trees + where it is O(log n). + +.. c:function:: itemtype *Z_pop(struct Z_head *) + + Remove and return the first item in the structure, or ``NULL`` if the + structure is empty. Like :c:func:`Z_first`, this is O(1) for all + data structures except red-black trees where it is O(log n) again. + + This function can be used to build queues (with unsorted structures) or + priority queues (with sorted structures.) + + Another common pattern is deleting all list items: + + .. code-block:: c + + while ((item = Z_pop(head))) + item_free(item); + + .. note:: + + This function can - and should - be used with hash tables. It is not + affected by the "modification while iterating" problem. To remove + all items from a hash table, use the loop demonstrated above. + +.. c:function:: itemtype *Z_next(struct Z_head *, itemtype *prev) + + Return the item that follows after ``prev``, or ``NULL`` if ``prev`` is + the last item. + + .. warning:: + + ``prev`` must not be ``NULL``! Use :c:func:`Z_next_safe()` if + ``prev`` might be ``NULL``. + +.. c:function:: itemtype *Z_next_safe(struct Z_head *, itemtype *prev) + + Same as :c:func:`Z_next()`, except that ``NULL`` is returned if + ``prev`` is ``NULL``. + +.. c:function:: itemtype *Z_del(struct Z_head *, itemtype *item) + + Remove ``item`` from the list and return it. + + .. note:: + + This function's behaviour is undefined if ``item`` is not actually + on the list. Some structures return ``NULL`` in this case while others + return ``item``. The function may also call ``assert()`` (but most + don't.) + +.. todo:: + + ``Z_del_after()`` / ``Z_del_hint()``? + +API for unsorted structures +--------------------------- + +Since the insertion position is not pre-defined for unsorted data, there +are several functions exposed to insert data: + +.. note:: + + ``item`` must not be ``NULL`` for any of the following functions. + +.. c:function:: DECLARE_XXX(Z, type, field) + + :param listtype XXX: ``LIST`` or ``ATOMLIST`` to select a data structure + implementation. + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add_head()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + +.. c:function:: void Z_add_head(struct Z_head *, itemtype *item) + + Insert an item at the beginning of the structure, before the first item. + This is an O(1) operation for non-atomic lists. + +.. c:function:: void Z_add_tail(struct Z_head *, itemtype *item) + + Insert an item at the end of the structure, after the last item. + This is also an O(1) operation for non-atomic lists. + +.. c:function:: void Z_add_after(struct Z_head *, itemtype *after, itemtype *item) + + Insert ``item`` behind ``after``. If ``after`` is ``NULL``, the item is + inserted at the beginning of the list as with :c:func:`Z_add_head`. + This is also an O(1) operation for non-atomic lists. + + A common pattern is to keep a "previous" pointer around while iterating: + + .. code-block:: c + + itemtype *prev = NULL, *item; + + for_each_safe(Z, head, item) { + if (something) { + Z_add_after(head, prev, item); + break; + } + prev = item; + } + + .. todo:: + + maybe flip the order of ``item`` & ``after``? + ``Z_add_after(head, item, after)`` + +API for sorted structures +------------------------- + +Sorted data structures do not need to have an insertion position specified, +therefore the insertion calls are different from unsorted lists. Also, +sorted lists can be searched for a value. + +.. c:function:: DECLARE_XXX_UNIQ(Z, type, field, compare_func) + + :param listtype XXX: One of the following: + ``SORTLIST`` (single-linked sorted list), ``SKIPLIST`` (skiplist), + ``RBTREE`` (RB-tree) or ``ATOMSORT`` (atomic single-linked list). + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + :param funcptr compare_func: Item comparison function, must have the + following function signature: + ``int function(const itemtype *, const itemtype*)``. This function + may be static if the list is only used in one file. + +.. c:function:: DECLARE_XXX_NONUNIQ(Z, type, field, compare_func) + + Same as above, but allow adding multiple items to the list that compare + as equal in ``compare_func``. Ordering between these items is undefined + and depends on the list implementation. + +.. c:function:: itemtype *Z_add(struct Z_head *, itemtype *item) + + Insert an item at the appropriate sorted position. If another item exists + in the list that compares as equal (``compare_func()`` == 0), ``item`` is + not inserted into the list and the already-existing item in the list is + returned. Otherwise, on successful insertion, ``NULL`` is returned. + + For ``_NONUNIQ`` lists, this function always returns NULL since ``item`` + can always be successfully added to the list. + +.. c:function:: itemtype *Z_find(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares equal to ``ref``. If no equal + item is found, return ``NULL``. + + This function is likely used with a temporary stack-allocated value for + ``ref`` like so: + + .. code-block:: c + + itemtype searchfor = { .foo = 123 }; + + itemtype *item = Z_find(head, &searchfor); + + .. note:: + + The ``Z_find()`` function is only available for lists that contain + unique items (i.e. ``DECLARE_XXX_UNIQ``.) This is because on a list + containing non-unique items, more than one item may compare as equal to + the item that is searched for. + +.. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares greater or equal to + ``ref``. See :c:func:`Z_find()` above. + +.. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares less than + ``ref``. See :c:func:`Z_find()` above. + + +API for hash tables +------------------- + +.. c:function:: DECLARE_XXX(Z, type, field, compare_func, hash_func) + + :param listtype XXX: Only ``HASH`` is currently available. + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + :param funcptr compare_func: Item comparison function, must have the + following function signature: + ``int function(const itemtype *, const itemtype*)``. This function + may be static if the list is only used in one file. For hash tables, + this function is only used to check for equality, the ordering is + ignored. + :param funcptr hash_func: Hash calculation function, must have the + following function signature: + ``uint32_t function(const itemtype *)``. The hash value for items + stored in a hash table is cached in each item, so this value need not + be cached by the user code. + + .. warning:: + + Items that compare as equal cannot be inserted. Refer to the notes + about sorted structures in the previous section. + +.. c:function:: void Z_init_size(struct Z_head *, size_t size) + + Same as :c:func:`Z_init()` but preset the minimum hash table to + ``size``. + +Hash tables also support :c:func:`Z_add()` and :c:func:`Z_find()` with +the same semantics as noted above. :c:func:`Z_find_gteq()` and +:c:func:`Z_find_lt()` are **not** provided for hash tables. + + +Atomic lists +------------ + +`atomlist.h` provides an unsorted and a sorted atomic single-linked list. +Since atomic memory accesses can be considerably slower than plain memory +accessses (depending on the CPU type), these lists should only be used where +neccessary. + +The following guarantees are provided regarding concurrent access: + +- the operations are lock-free but not wait-free. + + Lock-free means that it is impossible for all threads to be blocked. Some + thread will always make progress, regardless of what other threads do. (This + even includes a random thread being stopped by a debugger in a random + location.) + + Wait-free implies that the time any single thread might spend in one of the + calls is bounded. This is not provided here since it is not normally + relevant to practical operations. What this means is that if some thread is + hammering a particular list with requests, it is possible that another + thread is blocked for an extended time. The lock-free guarantee still + applies since the hammering thread is making progress. + +- without a RCU mechanism in place, the point of contention for atomic lists + is memory deallocation. As it is, **a rwlock is required for correct + operation**. The *read* lock must be held for all accesses, including + reading the list, adding items to the list, and removing items from the + list. The *write* lock must be acquired and released before deallocating + any list element. If this is not followed, an use-after-free can occur + as a MT race condition when an element gets deallocated while another + thread is accessing the list. + + .. note:: + + The *write* lock does not need to be held for deleting items from the + list, and there should not be any instructions between the + ``pthread_rwlock_wrlock`` and ``pthread_rwlock_unlock``. The write lock + is used as a sequence point, not as an exclusion mechanism. + +- insertion operations are always safe to do with the read lock held. + Added items are immediately visible after the insertion call returns and + should not be touched anymore. + +- when removing a *particular* (pre-determined) item, the caller must ensure + that no other thread is attempting to remove that same item. If this cannot + be guaranteed by architecture, a separate lock might need to be added. + +- concurrent `pop` calls are always safe to do with only the read lock held. + This does not fall under the previous rule since the `pop` call will select + the next item if the first is already being removed by another thread. + + **Deallocation locking still applies.** Assume another thread starts + reading the list, but gets task-switched by the kernel while reading the + first item. `pop` will happily remove and return that item. If it is + deallocated without acquiring and releasing the write lock, the other thread + will later resume execution and try to access the now-deleted element. + +- the list count should be considered an estimate. Since there might be + concurrent insertions or removals in progress, it might already be outdated + by the time the call returns. No attempt is made to have it be correct even + for a nanosecond. + +Overall, atomic lists are well-suited for MT queues; concurrent insertion, +iteration and removal operations will work with the read lock held. + +Code snippets +^^^^^^^^^^^^^ + +Iteration: + +.. code-block:: c + + struct item *i; + + pthread_rwlock_rdlock(&itemhead_rwlock); + for_each(itemlist, &itemhead, i) { + /* lock must remain held while iterating */ + ... + } + pthread_rwlock_unlock(&itemhead_rwlock); + +Head removal (pop) and deallocation: + +.. code-block:: c + + struct item *i; + + pthread_rwlock_rdlock(&itemhead_rwlock); + i = itemlist_pop(&itemhead); + pthread_rwlock_unlock(&itemhead_rwlock); + + /* i might still be visible for another thread doing an + * for_each() (but won't be returned by another pop()) */ + ... + + pthread_rwlock_wrlock(&itemhead_rwlock); + pthread_rwlock_unlock(&itemhead_rwlock); + /* i now guaranteed to be gone from the list. + * note nothing between wrlock() and unlock() */ + XFREE(MTYPE_ITEM, i); + +FRR lists +--------- + +.. TODO:: + + document + +BSD lists +--------- + +.. TODO:: + + refer to external docs diff --git a/doc/developer/ospf-api.rst b/doc/developer/ospf-api.rst index f4f38a63e9..41c31b29c3 100644 --- a/doc/developer/ospf-api.rst +++ b/doc/developer/ospf-api.rst @@ -4,76 +4,76 @@ OSPF API Documentation Disclaimer ---------- -The OSPF daemon contains an API for application access to the LSA -database. This API was created by Ralph Keller, originally as patch for -Zebra. Unfortunately, the page containing documentation of the API is no -longer online. This page is an attempt to recreate documentation for the -API (with lots of help of the WayBackMachine) +The OSPF daemon contains an API for application access to the LSA database. +This API and documentation was created by Ralph Keller, originally as patch for +Zebra. Unfortunately, the page containing documentation for the API is no +longer online. This page is an attempt to recreate documentation for the API +(with lots of help from the WayBackMachine). + +Ralph has kindly licensed this documentation under GPLv2+. Please preserve the +acknowledgements at the bottom of this document. Introduction ------------ -This page describes an API that allows external applications to access -the link-state database (LSDB) of the OSPF daemon. The implementation is -based on the OSPF code from FRRouting (forked from Quagga and formerly -Zebra) routing protocol suite and is subject to the GNU General Public -License. The OSPF API provides you with the following functionality: - -- Retrieval of the full or partial link-state database of the OSPF - daemon. This allows applications to obtain an exact copy of the LSDB - including router LSAs, network LSAs and so on. Whenever a new LSA - arrives at the OSPF daemon, the API module immediately informs the - application by sending a message. This way, the application is always - synchronized with the LSDB of the OSPF daemon. -- Origination of own opaque LSAs (of type 9, 10, or 11) which are then - distributed transparently to other routers within the flooding scope - and received by other applications through the OSPF API. - -Opaque LSAs, which are described in RFC 2370 , allow you to distribute -application-specific information within a network using the OSPF -protocol. The information contained in opaque LSAs is transparent for -the routing process but it can be processed by other modules such as -traffic engineering (e.g., MPLS-TE). +This page describes an API that allows external applications to access the +link-state database (LSDB) of the OSPF daemon. The implementation is based on +the OSPF code from FRRouting (forked from Quagga and formerly Zebra) routing +protocol suite and is subject to the GNU General Public License. The OSPF API +provides you with the following functionality: + +- Retrieval of the full or partial link-state database of the OSPF daemon. + This allows applications to obtain an exact copy of the LSDB including router + LSAs, network LSAs and so on. Whenever a new LSA arrives at the OSPF daemon, + the API module immediately informs the application by sending a message. This + way, the application is always synchronized with the LSDB of the OSPF daemon. +- Origination of own opaque LSAs (of type 9, 10, or 11) which are then + distributed transparently to other routers within the flooding scope and + received by other applications through the OSPF API. + +Opaque LSAs, which are described in :rfc:`2370`, allow you to distribute +application-specific information within a network using the OSPF protocol. The +information contained in opaque LSAs is transparent for the routing process but +it can be processed by other modules such as traffic engineering (e.g., +MPLS-TE). Architecture ------------ -The following picture depicts the architecture of the Quagga/Zebra -protocol suite. The OSPF daemon is extended with opaque LSA capabilities -and an API for external applications. The OSPF core module executes the -OSPF protocol by discovering neighbors and exchanging neighbor state. -The opaque module, implemented by Masahiko Endo, provides functions to -exchange opaque LSAs between routers. Opaque LSAs can be generated by -several modules such as the MPLS-TE module or the API server module. -These modules then invoke the opaque module to flood their data to -neighbors within the flooding scope. - -The client, which is an application potentially running on a different -node than the OSPF daemon, links against the OSPF API client library. -This client library establishes a socket connection with the API server -module of the OSPF daemon and uses this connection to retrieve LSAs and -originate opaque LSAs. +The following picture depicts the architecture of the Quagga/Zebra protocol +suite. The OSPF daemon is extended with opaque LSA capabilities and an API for +external applications. The OSPF core module executes the OSPF protocol by +discovering neighbors and exchanging neighbor state. The opaque module, +implemented by Masahiko Endo, provides functions to exchange opaque LSAs +between routers. Opaque LSAs can be generated by several modules such as the +MPLS-TE module or the API server module. These modules then invoke the opaque +module to flood their data to neighbors within the flooding scope. + +The client, which is an application potentially running on a different node +than the OSPF daemon, links against the OSPF API client library. This client +library establishes a socket connection with the API server module of the OSPF +daemon and uses this connection to retrieve LSAs and originate opaque LSAs. .. figure:: ../figures/ospf_api_architecture.png :alt: image image -The OSPF API server module works like any other internal opaque module -(such as the MPLS-TE module), but listens to connections from external -applications that want to communicate with the OSPF daemon. The API -server module can handle multiple clients concurrently. +The OSPF API server module works like any other internal opaque module (such as +the MPLS-TE module), but listens to connections from external applications that +want to communicate with the OSPF daemon. The API server module can handle +multiple clients concurrently. -One of the main objectives of the implementation is to make as little -changes to the existing Zebra code as possible. +One of the main objectives of the implementation is to make as little changes +to the existing Zebra code as possible. Installation & Configuration ---------------------------- -Download FRRouting and unpack +Download FRRouting and unpack it. -Configure your frr version (note that --enable-opaque-lsa also enables -the ospfapi server and ospfclient). +Configure and build FRR (note that ``--enable-opaque-lsa`` also enables the +ospfapi server and ospfclient). :: @@ -83,8 +83,8 @@ the ospfapi server and ospfclient). This should also compile the client library and sample application in ospfclient. -Make sure that you have enabled opaque LSAs in your configuration. Add -the ospf opaque-lsa statement to your ospfd.conf: +Make sure that you have enabled opaque LSAs in your configuration. Add the +``ospf opaque-lsa`` statement to your :file:`ospfd.conf`: :: @@ -108,171 +108,165 @@ Usage ----- In the following we describe how you can use the sample application to -originate opaque LSAs. The sample application first registers with the -OSPF daemon the opaque type it wants to inject and then waits until the -OSPF daemon is ready to accept opaque LSAs of that type. Then the client -application originates an opaque LSA, waits 10 seconds and then updates -the opaque LSA with new opaque data. After another 20 seconds, the -client application deletes the opaque LSA from the LSDB. If the clients -terminates unexpectedly, the OSPF API module will remove all the opaque -LSAs that the application registered. Since the opaque LSAs are flooded -to other routers, we will see the opaque LSAs in all routers according -to the flooding scope of the opaque LSA. +originate opaque LSAs. The sample application first registers with the OSPF +daemon the opaque type it wants to inject and then waits until the OSPF daemon +is ready to accept opaque LSAs of that type. Then the client application +originates an opaque LSA, waits 10 seconds and then updates the opaque LSA with +new opaque data. After another 20 seconds, the client application deletes the +opaque LSA from the LSDB. If the clients terminates unexpectedly, the OSPF API +module will remove all the opaque LSAs that the application registered. Since +the opaque LSAs are flooded to other routers, we will see the opaque LSAs in +all routers according to the flooding scope of the opaque LSA. We have a very simple demo setup, just two routers connected with an ATM -point-to-point link. Start the modified OSPF daemons on two adjacent -routers. First run on msr2: +point-to-point link. Start the modified OSPF daemons on two adjacent routers. +First run on msr2: -:: +.. code-block:: console - > msr2:/home/keller/ospfapi/zebra/ospfd# ./ospfd -f /usr/local/etc/ospfd.conf + # ./ospfd --apiserver -f /usr/local/etc/ospfd.conf And on the neighboring router msr3: -:: +.. code-block:: console - > msr3:/home/keller/ospfapi/zebra/ospfd# ./ospfd -f /usr/local/etc/ospfd.conf + # ./ospfd --apiserver -f /usr/local/etc/ospfd.conf Now the two routers form adjacency and start exchanging their databases. Looking at the OSPF daemon of msr2 (or msr3), you see this: -:: +.. code-block:: console - ospfd> show ip ospf database + ospfd> show ip ospf database - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Router Link States (Area 0.0.0.1) + Router Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum Link count - 10.0.0.1 10.0.0.1 55 0x80000003 0xc62f 2 - 10.0.0.2 10.0.0.2 55 0x80000003 0xe3e4 3 + Link ID ADV Router Age Seq# CkSum Link count + 10.0.0.1 10.0.0.1 55 0x80000003 0xc62f 2 + 10.0.0.2 10.0.0.2 55 0x80000003 0xe3e4 3 - Net Link States (Area 0.0.0.1) + Net Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum - 10.0.0.2 10.0.0.2 60 0x80000001 0x5fcb + Link ID ADV Router Age Seq# CkSum + 10.0.0.2 10.0.0.2 60 0x80000001 0x5fcb Now we start the sample main application that originates an opaque LSA. -:: +.. code-block:: console - > cd ospfapi/apiclient - > ./main msr2 10 250 20 0.0.0.0 0.0.0.1 + # cd ospfapi/apiclient + # ./main msr2 10 250 20 0.0.0.0 0.0.0.1 -This originates an opaque LSA of type 10 (area local), with opaque type -250 (experimental), opaque id of 20 (chosen arbitrarily), interface -address 0.0.0.0 (which is used only for opaque LSAs type 9), and area -0.0.0.1 +This originates an opaque LSA of type 10 (area local), with opaque type 250 +(experimental), opaque id of 20 (chosen arbitrarily), interface address 0.0.0.0 +(which is used only for opaque LSAs type 9), and area 0.0.0.1 Again looking at the OSPF database you see: -:: +.. code-block:: console - ospfd> show ip ospf database + ospfd> show ip ospf database - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Router Link States (Area 0.0.0.1) + Router Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum Link count - 10.0.0.1 10.0.0.1 437 0x80000003 0xc62f 2 - 10.0.0.2 10.0.0.2 437 0x80000003 0xe3e4 3 + Link ID ADV Router Age Seq# CkSum Link count + 10.0.0.1 10.0.0.1 437 0x80000003 0xc62f 2 + 10.0.0.2 10.0.0.2 437 0x80000003 0xe3e4 3 - Net Link States (Area 0.0.0.1) + Net Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum - 10.0.0.2 10.0.0.2 442 0x80000001 0x5fcb + Link ID ADV Router Age Seq# CkSum + 10.0.0.2 10.0.0.2 442 0x80000001 0x5fcb - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - Opaque-Type/Id ADV Router Age Seq# CkSum - 250.0.0.20 10.0.0.1 0 0x80000001 0x58a6 <=== opaque LSA + Opaque-Type/Id ADV Router Age Seq# CkSum + 250.0.0.20 10.0.0.1 0 0x80000001 0x58a6 <=== opaque LSA You can take a closer look at this opaque LSA: -:: +.. code-block:: console - ospfd> show ip ospf database opaque-area + ospfd> show ip ospf database opaque-area - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - LS age: 4 - Options: 66 - LS Type: Area-Local Opaque-LSA - Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) - Advertising Router: 10.0.0.1 - LS Seq Number: 80000001 - Checksum: 0x58a6 - Length: 24 - Opaque-Type 250 (Private/Experimental) - Opaque-ID 0x14 - Opaque-Info: 4 octets of data - Added using OSPF API: 4 octets of opaque data - Opaque data: 1 0 0 0 <==== counter is 1 + LS age: 4 + Options: 66 + LS Type: Area-Local Opaque-LSA + Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) + Advertising Router: 10.0.0.1 + LS Seq Number: 80000001 + Checksum: 0x58a6 + Length: 24 + Opaque-Type 250 (Private/Experimental) + Opaque-ID 0x14 + Opaque-Info: 4 octets of data + Added using OSPF API: 4 octets of opaque data + Opaque data: 1 0 0 0 <==== counter is 1 -Note that the main application updates the opaque LSA after 10 seconds, -then it looks as follows: +Note that the main application updates the opaque LSA after 10 seconds, then it +looks as follows: -:: +.. code-block:: console - ospfd> show ip ospf database opaque-area + ospfd> show ip ospf database opaque-area - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - LS age: 1 - Options: 66 - LS Type: Area-Local Opaque-LSA - Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) - Advertising Router: 10.0.0.1 - LS Seq Number: 80000002 - Checksum: 0x59a3 - Length: 24 - Opaque-Type 250 (Private/Experimental) - Opaque-ID 0x14 - Opaque-Info: 4 octets of data - Added using OSPF API: 4 octets of opaque data - Opaque data: 2 0 0 0 <==== counter is now 2 + LS age: 1 + Options: 66 + LS Type: Area-Local Opaque-LSA + Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) + Advertising Router: 10.0.0.1 + LS Seq Number: 80000002 + Checksum: 0x59a3 + Length: 24 + Opaque-Type 250 (Private/Experimental) + Opaque-ID 0x14 + Opaque-Info: 4 octets of data + Added using OSPF API: 4 octets of opaque data + Opaque data: 2 0 0 0 <==== counter is now 2 -Note that the payload of the opaque LSA has changed as you can see -above. +Note that the payload of the opaque LSA has changed as you can see above. -Then, again after another 20 seconds, the opaque LSA is flushed from the -LSDB. +Then, again after another 20 seconds, the opaque LSA is flushed from the LSDB. Important note: ^^^^^^^^^^^^^^^ In order to originate an opaque LSA, there must be at least one active -opaque-capable neighbor. Thus, you cannot originate opaque LSAs of no -neighbors are present. If you try to originate even so no neighbor is -ready, you will receive a not ready error message. The reason for this -restriction is that it might be possible that some routers have an -identical opaque LSA from a previous origination in their LSDB that -unfortunately could not be flushed due to a crash, and now if the router -comes up again and starts originating a new opaque LSA, the new opaque -LSA is considered older since it has a lower sequence number and is -ignored by other routers (that consider the stalled opaque LSA as more -recent). However, if the originating router first synchronizes the -database before originating opaque LSAs, it will detect the older opaque -LSA and can flush it first. +opaque-capable neighbor. Thus, you cannot originate opaque LSAs if no neighbors +are present. If you try to originate when no neighbors are ready, you will +receive a not ready error message. The reason for this restriction is that it +might be possible that some routers have an identical opaque LSA from a +previous origination in their LSDB that unfortunately could not be flushed due +to a crash, and now if the router comes up again and starts originating a new +opaque LSA, the new opaque LSA is considered older since it has a lower +sequence number and is ignored by other routers (that consider the stalled +opaque LSA as more recent). However, if the originating router first +synchronizes the database before originating opaque LSAs, it will detect the +older opaque LSA and can flush it first. Protocol and Message Formats ---------------------------- -If you are developing your own client application and you don't want to -make use of the client library (due to the GNU license restriction or -whatever reason), you can implement your own client-side message -handling. The OSPF API uses two connections between the client and the -OSPF API server: One connection is used for a synchronous request /reply -protocol and another connection is used for asynchronous notifications -(e.g., LSA update, neighbor status change). +If you are developing your own client application and you don't want to make +use of the client library (due to the GNU license restriction or whatever +reason), you can implement your own client-side message handling. The OSPF API +uses two connections between the client and the OSPF API server: One connection +is used for a synchronous request /reply protocol and another connection is +used for asynchronous notifications (e.g., LSA update, neighbor status change). Each message begins with the following header: @@ -326,8 +320,8 @@ The synchronous requests and replies have the following message formats: image -The origin field allows to select according to the following types of -origins: +The origin field allows origin-based filtering using the following origin +types: +-------------------------+---------+ | Origin | Value | @@ -339,7 +333,7 @@ origins: | ANY\_ORIGIN | 2 | +-------------------------+---------+ -The reply message has on of the following error codes: +The reply message has one of the following error codes: +--------------------------+---------+ | Error code | Value | @@ -372,16 +366,18 @@ The asynchronous notifications have the following message formats: image + +.. Do not delete these acknowledgements! + Original Acknowledgments from Ralph Keller ------------------------------------------ -I would like to thank Masahiko Endo, the author of the opaque LSA -extension module, for his great support. His wonderful ASCII graphs -explaining the internal workings of this code, and his invaluable input -proved to be crucial in designing a useful API for accessing the link -state database of the OSPF daemon. Once, he even decided to take the -plane from Tokyo to Zurich so that we could actually meet and have -face-to-face discussions, which was a lot of fun. Clearly, without -Masahiko no API would ever be completed. I also would like to thank -Daniel Bauer who wrote an opaque LSA implementation too and was willing +I would like to thank Masahiko Endo, the author of the opaque LSA extension +module, for his great support. His wonderful ASCII graphs explaining the +internal workings of this code, and his invaluable input proved to be crucial +in designing a useful API for accessing the link state database of the OSPF +daemon. Once, he even decided to take the plane from Tokyo to Zurich so that we +could actually meet and have face-to-face discussions, which was a lot of fun. +Clearly, without Masahiko no API would ever be completed. I also would like to +thank Daniel Bauer who wrote an opaque LSA implementation too and was willing to test the OSPF API code in one of his projects. diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 7ae48881ab..479aa04d59 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -30,6 +30,7 @@ dev_RSTFILES = \ doc/developer/include-compile.rst \ doc/developer/index.rst \ doc/developer/library.rst \ + doc/developer/lists.rst \ doc/developer/logging.rst \ doc/developer/maintainer-release-build.rst \ doc/developer/memtypes.rst \ |
