From 9758f47e4110fec1bf2421caa33d5f01ecf33276 Mon Sep 17 00:00:00 2001
From: Alessandro Rubini <rubini@gnudd.com>
Date: Wed, 20 Aug 2014 23:34:41 +0200
Subject: [PATCH] doc: added snmp-pain

Signed-off-by: Alessandro Rubini <rubini@gnudd.com>
---
 doc/snmp-pain.in | 660 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 660 insertions(+)
 create mode 100644 doc/snmp-pain.in

diff --git a/doc/snmp-pain.in b/doc/snmp-pain.in
new file mode 100644
index 000000000..aa86811de
--- /dev/null
+++ b/doc/snmp-pain.in
@@ -0,0 +1,660 @@
+\input texinfo    @c -*-texinfo-*-
+%
+% snmp-pain.in - main file for the documentation
+%
+%%%%
+
+%------------------------------------------------------------------------------
+%
+%                         NOTE FOR THE UNAWARE USER
+%                         =========================
+%
+%    This file is a texinfo source. It isn't the binary file of some strange
+%    editor of mine. If you want ASCII, you should "make snmp-pain.txt".
+%
+%------------------------------------------------------------------------------
+
+%
+% This is not a conventional info file...
+% I use three extra features:
+%   - The '%' as a comment marker, if at beginning of line ("\%" -> "%")
+%   - leading blanks are allowed (this is something I cannot live without)
+%   - braces are automatically escaped when they appear in example blocks
+%
+
+@comment %**start of header
+@documentlanguage en
+@documentencoding ISO-8859-1
+@setfilename snmp-pain.info
+@settitle snmp-pain
+@iftex
+@afourpaper
+@end iftex
+@paragraphindent none
+@comment %**end of header
+
+@setchapternewpage off
+
+@set update-month August 2014
+@c the release name below is substituted at build time
+@set release __RELEASE_GIT_ID__
+
+@finalout
+
+@titlepage
+@title SNMP Pain
+@subtitle Why (and how) I suffered while adding SNMP to White Rabbit
+@subtitle @value{update-month} (@value{release})
+
+@author Alessandro Rubini
+@end titlepage
+@headings single
+
+@c ##########################################################################
+@iftex
+@contents
+@end iftex
+
+@c ##########################################################################
+@node Top
+@top Introduction
+
+This summarizes my experience with @sc{snmp} and the WR switch, which
+has been a real pain to me.
+
+I know it's mainly me, as it looks like the rest of the world is
+happily using both @sc{snmp} as a protocol and @i{net-snmp} as a daemon.  I
+hope these notes may help other people within BE-CO-HT in finding a
+better way through @sc{snmp} and its implementations.
+
+This version of the document reflects the status of my knowledge and
+implementation as of @t{wr-switch-sw-v4.0}.
+
+As of Sep 2014 the document is included in the master branch of the
+repository because my mates consider is useful as refernce of what
+is there and why.  I'll try to keep it up to date with the various
+MIB object I add or modify.
+
+@c ##########################################################################
+@node The SNMP Protocol
+@chapter The SNMP Protocol
+
+The protocol itself is simple, as the name implies, but not really
+trivial to digest (unlike SMTP or HTTP, for example).  The datagrams
+are binary data: it makes sense for efficiency and to avoid writing a
+text parser in microcontrollers running the protocol, but it prevents
+testing using @t{telnet} or @t{wget}.
+
+The ``easiest'' testing tool is @i{snmpwalk}, which is bloated like
+the package it is part of. This is unavoidable for ``complete''
+implementations; my problem here is the lack of a simpler tool for
+an easy start. See @ref{Tools} for more details.
+
+@c =========================================================================
+@node Protocol Definition
+@section Protocol Definition
+
+@sc{snmp} is defined in the public RFC documents, but the list of them is
+too long to even list here -- it is really dozens of such documents.
+A good selection is found in the @t{doc/rfc} subdirectory of the
+@i{net-snmp} source archive.  Most of the relevant RFCs deal with
+defining MIB files (see @ref{The MIB Idea}), and the actual protocol
+bits (the frame format) is difficult to find.
+
+Actually, unlike most or all other IETF protocols, @sc{snmp} isn't really
+defined in any RFC, but relies on the @i{basic encoding rules} (ITU-T
+X.690) for the @i{abstract syntax notation} ASN.1 (i.e., again the MIB
+stuff).  So the huge lot of information out there tastes like a big loop of
+nothing, where everyone takes a lot for granted and refers to everyone
+else for details.  For me it was like learning Chinese relying on
+a number of Chinese dictionaries and nothing else.
+
+Eventually, after making sense of the protocol itself (mainly by
+sniffing and reverse engineering, while referring to the Chinese
+dictionaries any now and then), I finally changed my mind and agreed
+that it makes a good choice, and might be considered for the WR node
+as well (SPEC or equivalent). This however is not covered here.
+
+@c =========================================================================
+@node Basic Concepts
+@section Basic Concepts
+
+The network device runs an ``agent'' and the user runs a ``manager''.
+
+@sc{snmp} is based on the concept of management objects, that can be read
+or written.  So the requests a manager sends to agents are mainly
+``get'' and ``set''.
+
+Management objects are laid out in a tree. Thus an ``object
+identifier'' (OID) is like a pathname.  The textual representation of
+an OID uses a dot as a separator; the binary representation is an
+array of integers. For example WR owns the subtree
+``1.3.6.1.4.1.96.100'', where the ``...96'' is CERN and ``....1'' is
+for organizations. Identifiers are allocated by relevant organizations:
+CERN gave us our ``100'' and we are responsible for further levels.
+
+The manager can travel the whole tree or a subtree using a special
+``get next'' request.
+
+Additionally, an agent can send traps, but I have no experience with
+generating custom traps.
+
+@c =========================================================================
+@node The MIB Idea
+@section The MIB Idea
+
+The main aim of @sc{snmp} is being simple for the agent, while still
+remaining flexible. Thus the choice of a binary protocol and a get/set
+approach with an extensible tree of objects as described.
+
+When using a binary protocol on the wire and a simple pathname-based
+tree with integers as path components, the need for an higher-level
+description of the item arises immediately.  The solution adopted by
+@sc{snmp} designers is relying on a very-flexible and very-abstract notation:
+ASN.1, as already noted.
+
+Each and every management object is thus defined by a MIB, short for
+@i{management information base}. At least in the golden theory.  A MIB
+is a text file, with a very boresome structure, usually suffering from
+an over-engineering syndrome.  I must admit, though, that the thing
+was over-engineered enough to survive the decades passing: it allowed
+casting of a number of new and unexpected needs into this old and
+unpleasant abstract notation; modern and complex management tools can
+make sense of unforeseen management objects, thanks to their
+description found in the MIB file.
+
+The @sc{snmp} mantra is ``everything is a MIB''. Or ``just write the MIB
+and everything will automatically follow''. The theory says that
+the MIB text file can be parsed by the manager's application to show
+an user-friendly view of any object; it says that it can be parsed by
+the command-line tools so to use user-friendly names in the requests;
+it says that it can be parsed by the agent to create reply frames; that
+it can be parsed by a code-generator to fill he low-level bits. 
+
+So @sc{snmp} is mainly boring stuff for bureaucrats. Everything is hidden
+behind ASN.1: it either magically works or it magically fails; there
+is no clear documentation of the various levels of software or
+protocol, because it is claimed to ``just work'', which is not always
+true, despite theory.  Also, in practice sensible implementations avoid
+the suggested MIB-driven path.
+
+In the end, the @i{wr-switch-sw} package includes
+@t{WR-SWITCH-MIB.txt}, but clearly it's not true that this file is the
+whole of it.
+
+@c ##########################################################################
+@node Choosing an Agent
+@chapter Choosing an Agent
+
+When adding @sc{snmp} support to a network device, you should always at
+least include the standard management objects, the ones that every
+manager expects to find on the system.  This is the list of network
+interfaces, their features, their traffic statistics, and so on.
+
+Thus, writing your own is not an option: it doesn't make sense to have
+an incomplete wr-switch @sc{snmp} support, and re-implementing the huge
+number of standard management objects in a custom @sc{snmp} implementation
+is too big an effort to even consider it.
+
+I therefore looked at available free-software implementations.
+
+@c =========================================================================
+@node Requirements for WR
+@section Requirements for WR
+
+In order to choose a proper @sc{snmp} engine, we first need to know what
+we expect from it.  This a quick attempt at summarizing our requirements:
+
+@itemize @bullet
+
+@item We need support for all the basic actions, including traps, even if
+version 4 is not yet using them.
+
+@item We need to customize standard tables. This
+mainly involves VLAN support, because the WR switch needs a different
+backend than the normal kernel-driven Linux VLANs. Again, not yet
+implemented in version 4.
+
+@item We need to add a custom subtree, that includes both simple items
+and tables. Simple items are things such as the version string and the
+current time (the former is static and the latter is dynamic); tables
+are things such as the per-port statistic counters or the PTP slave
+list, if and when we choose it implement it.
+
+@end itemize
+
+@c =========================================================================
+@node net-snmp
+@section net-snmp
+
+Everybody, in the Unix world, is using @i{net-snmp}.
+
+This is a big and bloated implementation, using GNU autotools for
+configuration, shared libraries and everything else. It includes
+``simple'' tools for querying agents using the command line, and the
+usual bells and whistles.
+
+It is included in all Linux distributions, both hosted and embedded ones,
+and it really looks like the only choice available.  So we installed
+this one in the WR switch, by selecting the proper packages in our
+@i{buildroot} configuration.
+
+Documentation for @i{net-snmp} is mainly online: the promising @t{doc}
+subdirectory in the source tree only hosts the RFC documents, but
+fortunately @t{man/} includes documentation for the API and basic
+daemon use.  Tutorials and all the rest are available on the project
+site.  This means, among other things, that you can't access most
+documentation while off-line (like I am while writing this document)
+and you can't easily get documentation for the specific version you
+are using, excluding the manual pages.
+
+Still, the documentation is quite complete and well-done, though
+featuring the @sc{snmp}-wide misfeature of taking a lot for granted, it is
+useful for an expert user but not a great way to become one such beast.
+
+@c =========================================================================
+@node Other agents
+@section Other agents
+
+Simply put, there are no other choices other than @i{net-snmp}.  A
+number of proprietary implementations exist, but the free world seems
+bound to this implementation.
+
+(This section needs an update with the aid of a net search; I did it
+back then but since I found nothing I didn't save the results; and
+I'm now offline while writing this).
+
+@c =========================================================================
+@node Sub-agents
+@section Sub-agents
+
+Adding custom tables to an @sc{snmp} agent is a common requirement, so
+RFC-2741 defines the @i{AgentX} protocol.  The protocol is run on a
+local socket interface, and allows registering handlers for specific
+subtrees.  @i{net-snmp} supports the AgentX protocol, so this looked
+like an interesting option.
+
+This is the result of my evaluation:
+
+@itemize @bullet
+
+@item It is definitely not much used. I could not find any mainstream user
+of the feature; @t{lldpd} is the only one Debian package that names
+AgentX but it looks like the feature is not used in the Debian build.
+
+@item The code base supports both C and Perl bindings, the preferred
+one being Perl. Besides any performance concern, we can't easily run Perl
+on the WR switch because @i{buildroot} doesn't support it.
+
+@item It looks like support in @i{net-snmp} is incomplete, but I can't
+currently find the reference about this while being offline.
+
+@end itemize
+
+Still, AgentX looked promising, so I evaluated the thing.  The most
+interesting path being the various Python implementations: we already
+have Python in the WR Switch, because @i{buildroot} supports it and we
+needed it for the production test suite.
+
+There are three Python implementations, as far as I know:
+
+@table @code
+
+@item https://github.com/rayed/pyagentx
+
+	A 2013-2014 implementation, BSD license.  This only implements
+        ``get'' and ``get-next'' so it doesn't support our post-v4.0
+        needs.
+
+@item https://pypi.python.org/pypi/agentx/0.7
+
+	This is a 2010 GPL implementation. It looks a little too small
+        and simplified. I feared support dynamic tables herein would not
+        be easy.
+
+@item https://pypi.python.org/pypi/netsnmpagent/0.5.0
+
+	A 2012-2013 implementation, GPL3.  This seems serious, and it
+        paints itself as the result of a lot of frustration using the
+        available tools, something I sympathize with. It credits
+        @i{agentx} (preceding item in this list).  Unfortunately traps
+        are still missing.
+
+@end table
+
+The last option seems a viable one, but I eventually ruled it out
+because I'm not confident enough with Python to easily master it (for
+example, I'm sure I wouldn't be able to add traps in a reasonable time
+frame), and our WR management objects require a C language backend, at
+times -- which is another area where my Python lacks.
+
+Likely I was also scared by the length of the @t{SIMPLE-MIB.txt} it
+includes, even if in the end I wrote my MIB anyways.
+
+After evaluating sub-agents, I chose to stick to the shared-library
+mechanism offered by @i{net-snmp}, as described in later chapters.
+
+@c =========================================================================
+@node Tools
+@section Tools
+
+As said, I didn't find any simple tool to make @sc{snmp} queries. So I stuck
+to @t{snmpwalk}.  It's worth noting that @t{pysnmpwalk} (part
+of the @t{python-pysnmp4-apps} Debian package) works as well, with a
+compatible command line and output format.
+
+The reason why @t{snmpwalk} is not simple enough for me, is that it
+includes a MIB parser, in order to turn numeric pathnames and data into
+user-friendly names and values.
+
+I won't repeat here how to use the tool (see other documentation or
+the trivial examples in the @t{wr-switch-sw} manual), but I'll note
+that @t{-d} provides a dump of @sc{snmp} frames being sent and received,
+which is a good helper to understand what is happening under the hood,
+and hopefully avoiding a detailed read of the ITU specification for
+basic encoding.
+
+@c ##########################################################################
+@node Using the Code Generator
+@chapter Using the Code Generator
+
+What @i{net-snmp} suggests is the use of its own code generator,
+which outputs C sources built from a specified MIB file and command-line
+options. The generator is called @i{mib2c}.
+
+There are a number of drawbacks in using the generator, so I finally
+refrained from and chose to write code using the internal API
+
+@itemize @bullet
+
+@item The generated code includes parts that must be filled before it
+      can build and parts just marked as ``@t{XXX}'' but that otherwise
+      build. Thus, you really need to review the whole ``generated'' files
+      to bring them to a working state.
+
+@item Some of the calls to be filled are normal API calls, so you need
+      to be confident with @i{net-snmp} internal data structures; the
+      same effort you need to spend before you are able to write code
+      by yourself.
+
+@item The code is laid out as a number of big @t{switch} stanzas, without
+      relying on data structures. This makes editing the generated files
+      a heavy, repeating and failure-proof procedure.
+
+@item The generated code includes a number of repetitions, so the same edits
+      must be redone at least twice (like filling the same @t{switch}
+      construct in two different places.
+
+@item There are a number of options for the code generator: @i{mib2c}
+      uses a number of different templates, and I'm pretty sure not
+      all of them are used in practice; so I fear making the wrong choice
+      and finally hit bugs that are not my own.
+
+@item Experimenting several options to compare them is unfeasible, because
+      every edit to fill specific data structures must be redone each
+      time.  Similarly, you risk making the wrong choice and redo the
+      edits under a different template at a later time.
+
+@item @i{mib2c} leaks object names found in the MIB file into the source
+      code it outputs, and it does it everywhere in the output files. Thus,
+      during development, you'll need to redo your
+      edits several times to keep the C files in sync with a moving MIB
+      definition. And the usual trick of re-applying the same patch doesn't
+      always work, because the MIB names appear all around the generated
+      file.
+
+@item Documentation claims that the standard @i{net-snmp} modules are
+      based on @i{mib2c}, but while looking at the actual code I
+      didn't really found such traces. I admit that grepping for
+      ``@t{auto-generated by mib2c}'' on the source tree finds a
+      number of matches, but most of them look heavily edited, and
+      none of them matches my expectations of a ``simple'' file.
+
+@end itemize
+
+I used the generator for the initial trial, @i{wrsScalar}, as
+described in detail in @ref{wrsScalar}, but then I gave up. My failed
+experiments are still part of the @t{netsnmp-pain} branch, pushed to
+@t{ohwr.org}.
+
+For example, commit @t{ed1d654} uses the ``mib for dummies'' option of
+@i{mib2c} for the @t{pStats} table included in the local WR MIB file.
+This crated 4000 lines of source code, in 12 files, and 1000 lines of
+``README'' files. I would take days just to make sense of them.
+
+The final statistics source file, not using the generator, is 230
+lines of code.
+
+@c ##########################################################################
+@node Writing Real Code
+@chapter Writing Real Code
+
+In the end, I managed to make the thing work, using different
+approaches for the different objects in the WR subtree.  The objects
+currently defined are the following ones, all under
+@t{.1.3.6.1.4.1.96.100}, as defined in @t{WR-SWITCH-MIB.txt}:
+
+@example
+   wrsScalar     OBJECT IDENTIFIER ::= { wrSwitchMIB 1 }
+   wrsPstats     OBJECT IDENTIFIER ::= { wrSwitchMIB 2 }
+   wrsPpsi       OBJECT IDENTIFIER ::= { wrSwitchMIB 3 }
+   wrsVersion    OBJECT IDENTIFIER ::= { wrSwitchMIB 4 }
+   wrsDate       OBJECT IDENTIFIER ::= { wrSwitchMIB 5 }
+@end example
+
+All of the objects are read-only as of @t{wr-switch-sw-v4.0}.  The
+CamelCase naming convention matches what is found all over
+@t{net-snmp}.
+
+The various source files are built as a shared library; the
+configuration file of @t{snmpd} instructs it to load the library at
+run time.
+
+Documentation claims that the same source files can be compiled to run
+as an AgentX process or be directly linked to the @t{snmpd} binary.  I
+chose the shared-library build because this avoids patching
+@t{net-snmp} within @i{buildroot}: maintaining direct source files
+rather than patches is way easier.  I didn't feel safe in using the
+little-practiced AgentX protocol (especially for the upcoming ``set''
+queries). Last but not least, the shared library build is what
+Integrasys successfully used for version 2 of the WR switch.
+
+@c =========================================================================
+@node wrsScalar
+@section wrsScalar
+
+This is my first trial in making sense of the thing, and has no
+relevance for White Rabbit. The object is an integer that is
+incremented each time it is read.  The source file comes from @i{mib2c}
+using this ``intuitive'' sequence of commands (assuming you
+build @t{wr-switch-sw} and @t{WRS_OUTOUT_DIR} is available.
+
+@example
+   export BUILD_DIR="$WRS_OUTPUT_DIR/build/buildroot-2011.11/output/build"
+   export MIBDIRS=$BUILD_DIR/netsnmp-5.6.1.1/mibs
+
+   export MIBS=./WR-SWITCH-MIB.txt
+   $BUILD_DIR/netsnmp-5.6.1.1/local/mib2c \
+          -I $BUILD_DIR/netsnmp-5.6.1.1/local \
+          -c mib2c.scalar.conf \
+          wrsScalar
+@end example
+
+In practice, you need to set @t{MIBSDIR} and @t{MIBS} in the
+environment, so both your own MIB and the ``standard'' ones are
+available to the tool.  Standard MIBs are needed to get type
+definitions, using an ``import'' statement. Then you point your @i{-I}
+to @i{mib2c} inside @i{net-snmp} itself and execute the program from
+the same place.
+
+The integer itself, like all scalar values, is returned in
+item ``0'' of the subtree (thus, @t{1.3.6.1.4.1.96.100.1.0}).
+
+@c =========================================================================
+@node wrsPstats
+@section wrsPstats
+
+@c -------------------------------------------------------------------------
+@node The pStats Table
+@subsection The pStats Table
+
+This subtree, @t{wrSwitchMIB.2}, is a table.  All tables in @sc{snmp} are
+described as being made of ``lines'' and ``columns''. The columns are
+hardwired (in the MIB and in the code), and the lines can be dynamic
+(this matches how people usually write tables).
+
+The OID scanning, however, is reversed from our habit and the tables
+are returned column by column.  This happens because columns are
+defined in the MIB (each column is a directory, or a subtree, in the
+pathname of OIDs) while lines being dynamic can only appear as
+trailing items in the scanning.
+
+This implies, among other things, that any piece of code returning a
+dynamic table should build an internal data structure representing the
+whole table, in order to be able to consistently report the same lines
+for each column.
+
+Usually in a network-related table, the predefined columns represent
+the counters (tx/rx byte, errors, and so on) and each network
+interface is a line.  This approach allows the same MIB to work for
+every possible configuration.  For WR port statistics we chose a
+different approach: the counters themselves are somehow dynamic (they
+may change across versions, while the gateware develops) while the
+interfaces are restricted to be in the set @t{wr0}--@t{wr17}.
+
+So our pStats table is reversed from the common use of @sc{snmp} tables.
+As a side effect this allows the WR switch to return the name of each
+counter, in column 0. This allows greater flexibility when we'll have
+a new set of counters: the user-space tools will know the role of the
+new set of counters without any need to change them or the MIB file
+(we'll still need to change Switch @sc{snmp} code to match the new gateware.
+
+@c -------------------------------------------------------------------------
+@node pStats Code
+@subsection pStats Code
+
+After several distressing attempts with @i{mib2c}, still present in
+the history of the @i{netsnmp-pain} branch in the @t{wr-switch-sw}
+repository, I chose to base my code on the ``tcpTable'' implementation
+that is present in the core @i{net-snmp} implementation.  The TCP
+table does not use any @i{mib2c} template but is rather using directly
+the API.  To be exceedingly safe in my steps, I started by replicating
+the TCP table under the WRS subtree, and then I changed it
+step-by-step to support the counters. Each and every small commit is
+still in the @i{netsnmp-pain} branch, but the final source file was
+separately committed to @i{snmp-for-wrs}, which was later merged to
+@i{master}.
+
+As described in @ref{The pStats Table}, @sc{snmp} tables are first filled
+in local memory and then returned item-by-item to the network manager.
+Table filling is performed by @t{wrsPstats_load()}, registered by
+@t{init_wrsPstats()} using @t{netsnmp_inject_handler()}.  Unlike what
+happens in the TCP table, that allocates memory, I use static storage
+to load the counter values.  Then @t{wrsPstats_first_entry()}
+and @t{wrsPstats_next_entry()} are used to scan the table, building the
+indexes, but the actual value is returned by @t{wrsPstats_handler()}.
+I suspect the thing is not very efficient overall.
+
+One thing I found especially unpleasant in this implementation is the
+use of ``context pointers'' in looping through the table. The API
+supports the idea of a @t{loop_context} and @t{data_context}, but
+elsewhere the loop context is called @t{iterator_context}. This
+mismatch in naming in the tcpTable is now inherited in wrsPstats,
+but sooner or later I'll fix it. As a side effect, I now use
+the two contexts concurrently in an ambiguous way.  No, I'm not
+proud of this code.
+
+I don't feel confident with all the data structures as yet, and there
+still is some magic in all of this.  This is confirmed by a buglet in
+the current code, that makes @i{snmpwalk} always return one item after
+the end of the table -- most likely I need to fix @t{next_entry()}
+to return @t{NULL} earlier.
+
+@c =========================================================================
+@node wrsPpsi
+@section wrsPpsi
+
+This subtree was written in a hurry, and I feel likely we have some
+buglets; for example ``servo updates'' is the number of iterations,
+which will never exceed 32 bits, but it is reported using a 64-bit
+counter.  Moreover, not all management objects are actually filled,
+but I chose to nail down the MIB even if the code is not completely
+there.
+
+This @t{wrSwitchMIB.3} is split in two subtrees: @t{wrSwitchMIB.3.1}
+is an array of scalar values (all of them instantiated as @t{.0});
+@t{wrSwitchMIB.3.2} is a table.  Most functions in the code
+use @t{ppsi_g} for globals and @t{ppsi_p} for the per-port table.
+
+To keep the code compact and extensible, I chose to @t{popen(3)} a
+connection to existing tools.  The tools report information to
+@i{stdout} in a line-oriented tagged-format: ``@t{<key>: <value>\n}''.
+Thus, @t{wr_mon} now supports @t{-g} (``@t{SHOW_SNMP_GLOBALS}'') and
+@t{-p} (``@t{SHOW_SNMP_PORTS}'').  By pre-setting environment variables
+it's also possible to override the command names, for testing; see
+source code for details.
+
+Parsing is implemented using a @t{pickinfo} structure, where each key
+is associated to a data type, a pointer and a size.  This is is used
+to actually @t{sscanf} the value into a global structure.  The same
+``pickinfo'' table is later used to feed the binary data to @sc{snmp}.
+
+This parsing trick is concise and completely debugged/tested, so I
+plan to use to more widely when cleaning up and extending this WR @sc{snmp}
+support.
+
+The table of per-port values is scanned using the same steps as of
+@i{wrsPstats} (i.e. the tcpTable way, using the @i{net-snmp} iterator
+API).
+
+The global items are registered as a ``scalar group'' using the
+@i{net-snmp} API. I used @t{disman/expr/expScalars.c} as a reference
+and starting point. The function @t{ppsi_g_group()} refreshes the
+values (by calling @t{wr_mon -g}) whenever a request happens more than
+1 second later than the previous refresh. I'm aware this is a quick
+hack, but it works reliably without learning too many intricated API
+calls.
+
+@b{Note:} due to a bug in current @i{snmpwalk} implementations and
+64-bit values, 64-bit counters are returned with the two halves
+swapped.  Also, there is no ``signed'' 64-bit value defined anywhere
+in @sc{snmp}, thus the picosecond signed offset will be represented by
+tools as a huge number when it actually is a small negative value.
+
+@c =========================================================================
+@node wrsVersion
+@section wrsVersion
+
+The version is an array of scalars, so I used the ``scalar group''
+approach like the global PPSi values described in @ref{wrsPpsi}.
+
+The implementation is easier, because I rely on the fact that versions
+never change while the process runs. So I retrieve the version strings
+at initialization time, by calling ``@t{wrsw_version -t}'' (tagged)
+and parsing its @i{stdout}.  Parsing is easier than what we have in
+@i{wrsPpsi}, but my plan is having a unified parser overall, and eventually
+get rid of this simplified special case.
+
+@c =========================================================================
+@node wrsDate
+@section wrsDate
+
+This subtree includes two scalars: the TAI seconds as a ``counter64''
+value, and the human-readable equivalent string.
+
+The code uses a scalar group, as other subtrees described above already did.
+It maps @sc{fpga}  memory to get the WR date and return it as scalar @sc{snmp}
+values.
+
+@b{Note:} due to a bug in current @i{snmpwalk} implementations and
+64-bit values, the two halves of the 64-bit date are returned swapped.
+We feel returning a 32-bit value would be a worse choice, not being
+2038 safe.  When the bug is overall fixed, we'll be able to avoid
+word-swapping and be 2038-safe without changing the MIB file.
+
+
+@bye
+
+@c  LocalWords:  snmp wrSwitchMIB netsnmp ohwr snmpwalk AgentX buildroot
+@c  LocalWords:  pStats gateware wrsScalar wrsPstats wrsPpsi wrsVersion
+@c  LocalWords:  wrsDate subtree pathname
-- 
GitLab