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

Model

Product

Class

Ports

Reached over

gs110emx

GS110EMX

plus

10

NSDP

gs305ep

GS305EP

plus

5

NSDP

gs105pe

GS105PE

plus

5

NSDP

What it can do, per switch

[NSDP-1] (1,2,3)

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-2] (1,2,3)

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-3] (1,2,3)

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-4] (1,2,3)

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)

[NSDP-5] (1,2,3)

get_users is served only over CONSOLE, HTTP, SSH, TELNET

[NSDP-6] (1,2,3)

get_services is served only over CONSOLE, HTTP, SSH, TELNET

[NSDP-7] (1,2,3)

get_syslog is served only over CONSOLE, HTTP, SNMP, SSH, TELNET

[NSDP-8] (1,2,3)

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

[NSDP-9] (1,2,3,4,5,6,7,8,9)

NSDP has no PoE control tag (measured by an exhaustive tag sweep of a real GS110EMX, 10.1.5.25 fw 1.0.2.8, 2026-07-30 – see nsdp_read._SWEEP); use the HTTP backend for PoE

[NSDP-10] (1,2,3)

set_port_speed is served only over CONSOLE, HTTP, SSH, TELNET

[NSDP-11] (1,2,3)

set_flow_control is served only over CONSOLE, SSH, TELNET

[NSDP-12] (1,2,3)

set_syslog_enabled is served only over CONSOLE, SNMP, SSH, TELNET

[NSDP-13] (1,2,3)

add_syslog_collector is served only over CONSOLE, SSH, TELNET

[NSDP-14] (1,2,3)

remove_syslog_collector is served only over CONSOLE, HTTP, SNMP, SSH, TELNET

[NSDP-15] (1,2,3)

upload_certificate is served only over HTTP

[NSDP-16] (1,2,3)

upload_certificate_scp is served only over CONSOLE, SSH, TELNET

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 PASSWORD TLV (0x000A) “encrypted” by a repeating XOR against the 19-byte key NtgrSmartSwitchRock. 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 tag 0x001A alongside 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 0x001A is 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