NSDP¶
The Netgear Switch Discovery Protocol: a UDP, TLV-based management protocol, and one of only two ways to manage a Plus switch. The vendor documents none of it; everything here was established by capture and by measurement against live hardware.
Switches that speak it¶
What it can do, per switch¶
Operation |
gs110emx |
gs305ep |
gs105pe |
|---|---|---|---|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
— [NSDP-1] |
— [NSDP-1] |
— [NSDP-1] |
|
— [NSDP-2] |
— [NSDP-2] |
— [NSDP-2] |
|
— [NSDP-3] |
— [NSDP-3] |
— [NSDP-3] |
|
— [NSDP-4] |
— [NSDP-4] |
— [NSDP-4] |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
— [NSDP-5] |
— [NSDP-5] |
— [NSDP-5] |
|
— [NSDP-6] |
— [NSDP-6] |
— [NSDP-6] |
|
— [NSDP-7] |
— [NSDP-7] |
— [NSDP-7] |
|
✓ |
✓ |
✓ |
|
— [NSDP-8] |
— [NSDP-8] |
— [NSDP-8] |
|
— [NSDP-9] |
— [NSDP-9] |
— [NSDP-9] |
|
— [NSDP-9] |
— [NSDP-9] |
— [NSDP-9] |
|
— [NSDP-9] |
— [NSDP-9] |
— [NSDP-9] |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
NSDP has no LLDP neighbour tag (measured by an exhaustive NSDP tag sweep of a real GS110EMX (10.1.5.25, firmware 1.0.2.8, 2026-07-30) covering every tag in the 16-bit space; see nsdp_read.py for the full tag inventory)
NSDP has no MAC/FDB table tag (measured by an exhaustive NSDP tag sweep of a real GS110EMX (10.1.5.25, firmware 1.0.2.8, 2026-07-30) covering every tag in the 16-bit space; see nsdp_read.py for the full tag inventory)
NSDP has no PoE status tag (measured by an exhaustive NSDP tag sweep of a real GS110EMX (10.1.5.25, firmware 1.0.2.8, 2026-07-30) covering every tag in the 16-bit space; see nsdp_read.py for the full tag inventory); use the HTTP backend for PoE
NSDP has no environmental-sensor tag (measured by an exhaustive NSDP tag sweep of a real GS110EMX (10.1.5.25, firmware 1.0.2.8, 2026-07-30) covering every tag in the 16-bit space; see nsdp_read.py for the full tag inventory)
per-port admin-enable over NSDP is UNPROVEN on these Plus models: the measured tag inventory (GS110EMX fw 1.0.2.8) has two candidate per-port config tags (0x0800, 0x9400) whose semantics were never settled – no write has been attempted against either, and a wrong guess can drop the port’s link. Use the HTTP backend, whose port-settings page IS grounded
The wire format¶
A 32-byte header — signature NSDP, version, operation, error status, the
blamed TLV tag, the client and device MACs, a sequence number — followed by
type-length-value entries and the end marker ff ff 00 00. The library sends
from UDP port 63321 to port 63322.
+--------------------------------+
| "NSDP" header (32 bytes) |
| version / operation / error |
| blamed tag / client MAC |
| device MAC / sequence number |
+--------------------------------+
| TLV: tag(2) len(2) value(len) |
| TLV: ... |
| ff ff 00 00 |
+--------------------------------+
Because it is broadcast-based, the sending interface matters on a
multi-homed host. Pass nsdp_interface= to the facade, nsdp.interface in
the inventory, or --nsdp-interface on the command line.
Note
The error status names the TLV that caused it: header bytes 4–5 carry the blamed tag. That is the single most useful field when a write is rejected, and the library surfaces it.
What NSDP can and cannot do¶
Reads: port status, per-port statistics, VLANs, PVIDs, and the management IP. Writes: PVID, VLAN membership, VLAN create and delete, and the management IP.
It has no MAC/FDB table, LLDP, sensor or PoE capability — not an assumption, but the finding of an exhaustive sweep of the tag space against a live GS110EMX. The refusal messages the library raises say so, and they name that sweep as their evidence.
Port administrative enable is also refused, as unproven rather than absent: no tag has been shown to do it.
nsdp_device returns the complete raw device record — firmware,
serial number, DHCP mode, VLAN engine, QoS engine, port mirroring, IGMP
snooping, broadcast filtering, loop detection — including fields no other
backend exposes, and with per-port values left unconverted.
Write authentication¶
Tag 0x0014 (AUTH_V2_ENCPASS) advertises which of the two schemes a
switch wants: 1 means v1, 0x10 means v2.
- v1 (older firmware)
The admin password travels in a
PASSWORDTLV (0x000A) “encrypted” by a repeating XOR against the 19-byte keyNtgrSmartSwitchRock. XOR is its own inverse, so one function both encodes and decodes.- v2 (newer firmware, including GS110EMX 1.0.2.8)
A challenge-response. The client reads a fresh 4-byte salt from tag
0x0017— which rotates on every read — then writes an 8-byte token in tag0x001Aalongside the configuration change. The token is not a hash: it is an 8-byte XOR fold of the 20-byte password, the 4-byte salt and the switch’s own 6-byte MAC, taken from the salt read’s response header. Each output byte XORs three password bytes — the weakness documented as CVE-2020-35221.
Warning
The token TLV must come first, before the configuration TLVs. Sending it last is rejected. This cost real debugging time and is pinned by a test.
Two facts about v2, both easy to get wrong by assumption:
The token is not
md5(merge(password, salt)). That transform is what the switch’s web UI uses — confirmed by a successful HTTP login — but the two authentication paths do not share it. Every md5 variant was tried live against a real unit and rejected with error 13.Tag
0x001Ais write-only: reading it returns error 3.
Cross-checked against other implementations¶
The v2 fold reproduces go-nsdp’s own test vector byte for byte, and two
independent third-party tools decoded the mock’s packets. ProSafeLinux decodes
the GS110EMX and GS105PE mocks completely, with every value matching the seed
(the third NSDP model, the GS305EP, is seeded from invention rather than a
capture, so decoding it would prove nothing); the C
implementation ngadmin validated the header and surfaced a real four-byte
sequence-number bug, since fixed.
That is the point of an independent cross-check: a mock validated only by the client that talks to it proves nothing.
Gotchas¶
Every 10G link reports as down. In PORT_STATUS, the speed byte value
0x06 means 10 Gbit/s; treating unknown values as “down” made every 10G link
on the GS110EMX read that way — a real defect the tag sweep found.
The same TLV is not the same length on every firmware. PORT_MIRRORING
varies between firmware versions; parsers must not assume a fixed size.
A device reply without a model is refused. A get_device response missing
its MODEL TLV is not a valid identification, and the parser says so rather
than inventing one.
API¶
netgear_switch.protocols.nsdp.protocol— header, tags and error codes.netgear_switch.protocols.nsdp.auth— v1 XOR and the v2 fold, with the full investigation record in the module docstring.netgear_switch.protocols.nsdp.types—NsdpDeviceand its components.