|
NetBurner 3.5.7
PDF Version |
Example Path: examples/snmp/CustomMIB
This example demonstrates SNMP (Simple Network Management Protocol) agent functionality on a NetBurner device. It implements a basic SNMP agent with three custom enterprise MIB variables and provides interactive command interfaces via serial console and Telnet.
The example defines three enterprise MIB variables under OID 1.3.6.1.4.1.8174 (NetBurner enterprise number). These are defined in Nburn_Cname_Mib.txt and implemented in Nburn_Cname_Mib.cpp.
These variables allow remote SNMP managers to read and modify the device's SNMP community strings and trap destination address. Changes are persisted to flash storage via SaveConfigToStorage().
The "Read-Write" column above is the MIB-level access – what the variable is conceptually capable of. At runtime each request is also filtered by an access mask tied to which community string the manager presented; see "SNMP Communities and Access Masks" below for what that means in practice and why WRITECOMMUNITY is gated differently than the other two.
SNMPv1/v2c authenticates each request with a community string – a plaintext shared secret sent with every PDU. The agent recognizes two of them, configured in TheSnmpConfig and persisted to flash:
-c value used by snmpget / snmpwalk) is the "read-only password". It authorizes Get / GetNext requests.snmpset. A manager that knows the write community can do everything the read community can do, plus issue Sets.When a request arrives, the agent decodes the community string into a bitmask (see DefaultCommunityDecode in nbrtos/source/snmp/snmp.cpp):
That bitmask is then ANDed against the access mask each leaf was registered with. The two masks therefore mean:
| Mask | A leaf with this mask is reachable by ... |
|---|---|
READ_COMMUNITY_MASK | the read community OR the write community. |
WRITE_COMMUNITY_MASK | only the write community. |
The usual pairing is SNMPREADFUNC(..., READ_COMMUNITY_MASK) plus SNMPWRITEFUNC(..., WRITE_COMMUNITY_MASK) so that read-community clients can Get the value but only write-community clients can Set it. READCOMMUNITY and TRAPDESTINATION follow that pattern. When the mask rejects a request, the agent does not return a permission error – it behaves as if the OID is not there, and net-snmp surfaces that as No Such Object available on this agent at this OID.
WRITECOMMUNITY is the one leaf in this example whose read side is also gated on WRITE_COMMUNITY_MASK:
The reason is privilege escalation. The value of WRITECOMMUNITY is the credential a manager needs to perform Sets. If a read-community client could Get this leaf, they would learn the write community and promote themselves from read-only to read-write – the read community would effectively confer write access. Requiring the write community on the read side ensures only a manager who already holds the write credential can read it back, so they learn nothing they did not already know. (The trade-off is the misleading No Such Object message described below; that is acceptable, leaking the password is not.)
In every case below, <write-community> must be the current write community. The example ships with both communities defaulted to public, so the asymmetry is invisible until you change WRITECOMMUNITY to a different value.
Read it – verify the current write community:
snmpget -v2c -c <write-community> 192.168.1.100 .1.3.6.1.4.1.8174.2.0
Using the read community here returns No Such Object available on this agent at this OID. That is the access mask rejecting the request, not a missing OID – expected behaviour, not a bug.
Change it – rotate the write community:
snmpset -v2c -c <write-community> 192.168.1.100 .1.3.6.1.4.1.8174.2.0 s "newWriteCommunity"
Every subsequent Set, and every subsequent Get of WRITECOMMUNITY.0, must use the new value in -c. If you forget it there is no SNMP path back in – recover via the local web UI, the serial console, or a factory reset of the device's stored config.
SNMP support is enabled by the predef-overload.h file located at:
overload/nbrtos/include/predef-overload.h
which contains:
#define ENABLE_SNMP (1)
Initialization (main.cpp - UserMain)
The application starts in UserMain() and performs the following:
The SNMP agent is started automatically by the global SnmpServlet object (TheSnmpServer) which listens on UDP port 161 – merely declaring it at file scope is enough to bring the agent online.
Command Processor (commands.cpp)
The NetBurner command processor provides an interactive CLI accessible through both the serial console and Telnet (port 23). All five callbacks are file-scope inside commands.cpp; main.cpp never sees their names. A single public function – StartCommandLine() in commands.h – wires them up and starts the task.
"NB:<type>>" prompt.Sessions time out after 60 seconds of inactivity.
Custom MIB Variables (Nburn_Cname_Mib.cpp)
Each MIB variable has a read function and a write function registered with the SNMP framework via SNMPREADFUNC and SNMPWRITEFUNC macros.
Read functions return the current value from TheSnmpConfig, the global SNMP configuration object that provides persistent storage.
Write functions validate the new value (in the bTest pass) and then apply it to TheSnmpConfig and call SaveConfigToStorage() to persist the change to flash.
SNMP Traps (trap.cpp + trap.h)
The SendTestTrap() function sends an enterprise-specific SNMP trap (trap type 6, specific code 1) to the configured trap destination. The internal file-scope TrapVarFunction() callback adds two custom variable bindings containing test messages to the trap PDU using ASN.1 encoding.
trap.h is included by both commands.cpp (for the 'T' command) and web.cpp (for the trap-button POST handler) – one definition, two call sites, no forward declarations leaking through main.cpp.
Web UI (web.cpp + html/index.html + htmlvar.h)
The HTTP server (started by StartHttp() in UserMain) serves html/index.html, which displays:
Mechanics:
<!--CPPCALL FunctionName --> in the HTML calls a C++ function named FunctionName(int sock, PCSTR url) in web.cpp; the function writes its output to the socket via fdprintf().<!--VARIABLE Secs --> evaluates the Secs global at request time; the symbol must be visible to the generated htmldata.cpp via htmlvar.h (which includes <nbrtos.h>).At build time, the makefile's comphtml html -osrc/htmldata.cpp step walks html/ and converts every file (HTML, images, CSS) into a single src/htmldata.cpp that is compiled and linked into the firmware – there is no filesystem at runtime.
Most applications want SNMP scalar variables that a manager can Get and Set. The three entries in Nburn_Cname_Mib.cpp (READCOMMUNITY, WRITECOMMUNITY, TRAPDESTINATION) are exactly this pattern – a read-function plus an optional write-function, registered with the SNMPREADFUNC / SNMPWRITEFUNC macros. Delete those three and put your own in their place.
Replace the contents of src/Nburn_Cname_Mib.cpp with the following (keep everything else in the project as-is):
Build and load. In iReasoning (or any SNMP manager), do a Get on 1.3.6.1.4.1.8174.10.1.0 – you should see 3300. A Get on ...10.2.0 returns 0. Set ...10.2.0 to 1, then Get it again to confirm. A Walk of 1.3.6.1.4.1.8174.10 will show both.
All of these are just typedefs – pick the one that matches the value semantics:
| Macro type | C type | Use for |
|---|---|---|
ASN_typeInteger32 | int | signed integers, on/off flags |
ASN_typeUnsigned | unsigned int | unsigned readings (0..2^32-1) |
ASN_typeGauge | int | instantaneous values that go up/down |
ASN_typeCounter | unsigned int | monotonically-increasing counts |
ASN_typeString | const char * | strings |
ASN_typeIpAddr | unsigned int | IPv4 address |
The rule is: the read function's return type and the write function's parameter type must match the typedef that corresponds to the macro. Example pairings live at the top of snmp/include/snmp_table.h if you want to see them all.
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\Nburn_Cname_Mib.txt into that mibs folder, the tools will resolve symbolic names like READCOMMUNITY.0, WRITECOMMUNITY.0, and TRAPDESTINATION.0 from NBURNSAMPLE-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).
set PATH=C:\nburn\snmp\net-snmp;PATH% set MIBDIRS=C:\nburn\snmp\mibs set MIBS=ALL
Equivalent in PowerShell:
$env:PATH = "C:\nburn\snmp\net-snmp;$env:PATH" $env:MIBDIRS = "C:\nburn\snmp\mibs" $env:MIBS = "ALL"
In a Bash shell (Git Bash / MSYS) use Unix-style paths:
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.
By symbolic name (after copying Nburn_Cname_Mib.txt into the mibs folder):
snmpget -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 READCOMMUNITY.0 WRITECOMMUNITY.0 TRAPDESTINATION.0
Expected output:
NBURNSAMPLE-MIBREADCOMMUNITY.0 = STRING: "public" NBURNSAMPLE-MIBWRITECOMMUNITY.0 = STRING: "public" NBURNSAMPLE-MIBTRAPDESTINATION.0 = IpAddress: 0.0.0.0
By numeric OID (no MIB needed):
snmpget -v2c -c public 192.168.1.100 .1.3.6.1.4.1.8174.1.0 .1.3.6.1.4.1.8174.2.0 .1.3.6.1.4.1.8174.3.0
Standard MIB-II values served by the NetBurner agent:
snmpget -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 sysDescr.0 sysUpTime.0 sysObjectID.0
Type letter goes between the OID and the value: s = string, i = integer, a = IpAddress, u = unsigned, c = counter, t = TimeTicks, o = OID, x = hex string
Set a community string (string type, letter s):
snmpset -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 READCOMMUNITY.0 s "newcommunity"
snmpset -v2c -c public 192.168.1.100 .1.3.6.1.4.1.8174.1.0 s "newcommunity"
Set the trap destination (IpAddress type, letter a):
snmpset -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 TRAPDESTINATION.0 a 192.168.1.7
snmpget -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 TRAPDESTINATION.0
Important: writes go through the write community, not the read community. This example ships with both defaulted to public, so -c public works either way. As soon as you change WRITECOMMUNITY.0 to something else, all later snmpset commands must use that new community string in -c.
Walk just the NetBurner enterprise tree:
snmpwalk -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 .1.3.6.1.4.1.8174
Walk standard MIB-II system + interfaces:
snmpwalk -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 system snmpwalk -v2c -c public -M C:\nburn\snmp\mibs -m ALL 192.168.1.100 interfaces
Useful when something does not respond and you want to confirm the MIB is loaded correctly:
snmptranslate -M C:\nburn\snmp\mibs -m ALL -On READCOMMUNITY.0 snmptranslate -M C:\nburn\snmp\mibs -m ALL .1.3.6.1.4.1.8174.1.0
Cannot find module (NBURNSAMPLE-MIB) Copy this example's mib\Nburn_Cname_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: (readOnly) You used the read community on a write operation, or you used the old WRITECOMMUNITY value after changing it. Use the current value of WRITECOMMUNITY in -c for snmpset.
No Such Object available on this agent at this OID The OID is not registered, or the access mask rejected the request under the community you used. Check what community the leaf was registered with in the SNMPREADFUNC/SNMPWRITEFUNC macro.
sysDescr.0 : "NetBurner SNMP Test application" sysObjectID.0 : 1.3.6.1.4.1.8174.2.40