|
NetBurner 3.5.7
PDF Version |
Example Path: examples/snmp/TableRead
This example demonstrates a read-only SNMP table MIB on a NetBurner device. Eight simulated sensors are registered with the SNMP agent at startup, and a background task in UserMain writes fresh readings into the per-row data structs every few seconds. SNMP GET / GET-NEXT / walk requests pick up the latest values automatically; nothing about the SNMP tree is ever re-registered after startup.
Use this pattern when you have repeating measured data (one row per sensor, per RS-485 board, per port) that an external manager only ever needs to read. Read-only access is enforced simply by what we never register: the code uses AddSnmpEntry to expose each column for reading, and never calls AddSnmpWriteEntry.
The CustomMIB example shows how to wire up individual scalars with SNMPREADFUNC(name, "oid.literal", type, fn, mask). That macro is fine for true singletons – read community, write community, trap destination – but it does not scale:
SNMPREADFUNC bakes the OID into the macro as a string literal, so each variable needs its own line.snmp_typeXxx ReadFunc()), with no void * context telling the function which OID is being read. A single function therefore cannot serve many OIDs, ruling out data-driven registration.For a device polling, say, 12 RS-485 boards x 12 parameters each (~150 OIDs), the macro form forces 150+ hand-written functions and macro lines – repetitive and impossible to drive from data.
The SNMP table is the standard idiom for "N instances of the same columns". The NetBurner runtime exposes the underlying registration APIs that the macro wraps:
| API | Purpose |
|---|---|
AddSnmpEntry(subid, prefix, len, suffix, fn, data_el, m) | Register one column of one row |
AddSnmpWriteEntry(...) | Register a writable column |
SnmpRemoveTableElement(prefix, data_el) | Remove a row at runtime |
The handler signatures carry the context the macro form lacks:
data_el – opaque pointer to your row data (e.g. one board's struct)subid – column number within the row
One handler serves every OID in the table.** Registering N rows is a simple loop; updating values is just writing into a struct.
SNMPREADFUNCKeep the scalar macros for true singletons that have no natural index:
Use a table for any repeating shape (rows of sensors, ports, boards, sessions, connections). This example uses one read-only scalar (sensorTableNumber – the row count) plus a full sensor table to illustrate both styles side by side.
1.3.6.1.4.1.8174.20.1.* with eight read-only columns (Index, Name, Temperature, Humidity, Pressure, Uptime, ReadCount, Status), populated with 8 simulated sensors at startupsensorTableNumber (1.3.6.1.4.1.8174.19.0) reporting the row countUserMain that updates the per-row struct fields every 5 seconds (simulating an RS-485 poll loop)Defined in mib/NburnSensorTable_Mib.txt and implemented in src/sensor.cpp.
Scalar:
Table (under 1.3.6.1.4.1.8174.20):
Columns within sensorEntry:
SNMP support is enabled by the predef-overload.h file located at:
overload/nbrtos/include/predef-overload.h
which contains:
#define ENABLE_SNMP (1)
The table is built around three pieces in src/sensor.cpp, exposed via src/sensor.h:
AddTableElementsensorEntry(void *data_el, snmp_typeINTEGER index) For one row, calls AddSnmpEntry(...) once per column. The same opaque data_el pointer (the SensorData* for that row) is handed to the dispatcher on every callback. No AddSnmpWriteEntry calls appear anywhere – read-only enforcement comes from what is never registered. This function is file-scope; the public entry is InitSensors() below.PutTableElementssensorEntry(SNMP_Request &req, void *data_el, int subid) The single read dispatcher. data_el identifies the row; subid (1..8) identifies the column. It writes the right ASN.1 type into req.put_asn for that field. Also file-scope.InitSensors() and PollSensors() (declared in sensor.h) InitSensors runs once from UserMain and registers all 8 rows. PollSensors runs every 5 seconds inside UserMain's idle loop and just writes fresh values into Sensors[i].*. No SNMP plumbing is touched after InitSensors returns.This is the model recommended for systems that poll N similar data sources: build the tree once, then keep updating the row structs.
The CLI ('D' to dump the SNMP tree, 'T' to send a trap, logout to end the session) lives in src/commands.cpp, exposed through a single public function StartCommandLine() in src/commands.h. The trap PDU itself is built in src/trap.cpp; commands.cpp includes trap.h and calls SendTestTrap() when the user types 'T'.
The web UI in html/index.html and src/web.cpp is a different view of the same data: a single HTML page with a <meta http-equiv="refresh" content="5"> tag so the browser re-fetches every 5 seconds (matching the device's polling cadence). The page contains:
<!--VARIABLE Secs -->.<!--CPPCALL ShowSensorRows -->. The handler in web.cpp delegates to WriteSensorTableRows() in sensor.cpp; that function iterates Sensors[] and writes one <tr class="ok|warn|fault">...</tr> per row. CSS rules in the page color the rows green / yellow / red based on the class.<form action="trap" method="post"> whose submit button fires SendTestTrap() – the same call the 'T' CLI command uses. The POST callback in web.cpp redirects back to /index.html on completion and a counter shows how many traps have been sent.Encapsulation: the SensorData struct stays private to sensor.cpp; web.cpp only sees the WriteSensorTableRows(int sock) declaration in sensor.h. This keeps the row-data layout free to evolve without touching the web layer.
Connect via serial console or Telnet to port 23. Log in with any username and password that are not identical (e.g., user: "hello", pass: "world").
The NetBurner SDK ships a portable net-snmp 5.9.1 distribution at:
C:\nburn\snmp\net-snmp\
containing snmpget.exe, snmpset.exe, snmpwalk.exe, snmptable.exe, snmptranslate.exe, etc. The standard MIBs (RFC1155-SMI, RFC1213-MIB, SNMPv2-SMI, SNMPv2-TC, ...) live alongside the per-example MIBs at:
C:\nburn\snmp\mibs\
If you copy this example's mib\NburnSensorTable_Mib.txt into that mibs folder, the tools will resolve symbolic names like sensorTableNumber.0, sensorTemperature.5, sensorPressure.7, etc. from NBURNSENSOR-MIB. Without the copy you can still use full numeric OIDs and everything works.
All examples below use the example IP address 192.168.1.100. Substitute your NetBurner's actual IP (run nbdiscovery if you do not know it).
Windows cmd:
set PATH=C:\nburn\snmp\net-snmp;PATH% set MIBDIRS=C:\nburn\snmp\mibs set MIBS=ALL
PowerShell:
$env:PATH = "C:\nburn\snmp\net-snmp;$env:PATH" $env:MIBDIRS = "C:\nburn\snmp\mibs" $env:MIBS = "ALL"
Bash (Git Bash / MSYS):
export PATH="/c/nburn/snmp/net-snmp:$PATH" export MIBDIRS="/c/nburn/snmp/mibs" export MIBS="ALL"
After that the -M / -m flags below can be omitted. All examples include them explicitly so they work even without the env vars.
The row count scalar:
snmpget -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 sensorTableNumber.0
Expected output:
NBURNSENSOR-MIBsensorTableNumber.0 = INTEGER: 8
Pick specific cells (column.row syntax):
snmpget -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 sensorName.3 snmpget -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 sensorTemperature.5 sensorPressure.7 snmpget -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 sensorStatus.0 sensorReadCount.0 sensorUptime.0
Numeric form (no MIB needed):
snmpget -v2c -c public 192.168.1.100 .1.3.6.1.4.1.8174.19.0 snmpget -v2c -c public 192.168.1.100 .1.3.6.1.4.1.8174.20.1.2.3
Walk the entire sensor table (8 columns x 8 rows = 64 OIDs):
snmpwalk -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 sensorTable
Walk both the scalar and the table:
snmpwalk -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 .1.3.6.1.4.1.8174
Walk the table twice ~10 seconds apart and observe that sensorTemperature, sensorReadCount, and sensorUptime change between walks – that is the polling task updating the row structs.
Much easier to read than a column-major snmpwalk:
snmptable -v2c -c public -M C:\nburn\snmp\mibs -m ALL -Cb 192.168.1.100 sensorTable
Expected output (values jitter between polls):
SNMP table: NBURNSENSOR-MIBsensorTable
Index Name Temperature Humidity Pressure Uptime ReadCount Status 0 "Sensor #0" 2230 41 10146 0:0:00:28.65 5 1 1 "Sensor #1" 2281 42 10150 0:0:00:28.65 5 1 2 "Sensor #2" 2300 43 10154 0:0:00:28.65 5 1 3 "Sensor #3" 2351 44 10158 0:0:00:28.65 5 1 ...
Every column is read-only. A SET on any cell must be rejected:
snmpset -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 sensorTemperature.0 i 9999
Expected response:
Error in packet. Reason: notWritable (That object does not support modification) Failed object: NBURNSENSOR-MIBsensorTemperature.0
Read-only enforcement comes from what sensor.cpp never registers – there are AddSnmpEntry() calls but no AddSnmpWriteEntry() calls.
snmptranslate -M C:\nburn\snmp\mibs -m ALL -On sensorTableNumber.0 snmptranslate -M C:\nburn\snmp\mibs -m ALL .1.3.6.1.4.1.8174.20.1.3.0
Cannot find module (NBURNSENSOR-MIB) Copy this example's mib\NburnSensorTable_Mib.txt to C:\nburn\snmp\mibs\ and retry.
Timeout Verify with ping 192.168.1.100 and confirm the device is on the same subnet. SNMP listens on UDP/161; check Windows Firewall.
Error in packet, Reason: notWritable You attempted to SET a read-only column. Every column in this table is read-only by design.
Same values returned every poll The polling loop updates the row structs every 5 seconds. If sensorReadCount and sensorUptime never increment, check that UserMain reached the while(1) { OSTimeDly(...); PollSensors(); } loop.
sysDescr.0 : "NetBurner SNMP Table Read example" sysObjectID.0 : 1.3.6.1.4.1.8174.2.41