Kemp’s Blog

A technical blog about technical things

DeviceMaster protocol, part 1: UDP protocol

The DeviceMaster range, at least with current firmware and drivers, uses a UDP-based protocol for discovery. The obvious first step, then, was to start Wireshark and log a few scans performed via PortVision DX. The Scan function within PortVision DX sent messages of the following form to the UDP broadcast address 255.255.255.255 on port 4606:

a9 8d fd 53 7a 1c b0 de 00 00 2c 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

A lot of zeros there, not much to go on. Performing more scans showed the the 0x2c in byte 11 was incrementing, so this was presumably a sequence number of some kind. Leaving PortVision DX open overnight showed that it was a 16 bit count in little-endian byte order. The 0x0c byte could be specifying a function—in this case a scan.

I also happened to perform a Refresh on my DeviceMaster device, which had already been discovered, and noticed that this sent an almost identical message. I had assumed the refresh would be over the TCP-based protocol as it is communicating with a specific device, but apparently not. The message I saw for a Refresh was also sent to the broadcast address on port 4606 but looked like this:

a9 8d fd 53 7a 1c b0 de 00 00 3d 00 0c 00 16 06 00 c0 4e 08 37 f5 00 00 00 00 00 00 00 00 00 00

Some new values! Given this this was broadcast in the same way as a scan message the extra values must specify the device that is being refreshed, with any other devices ignoring the message. Sure enough, the MAC address of my device is 00:c0:4e:08:37:f5, which matches the last six of the eight new values. At this point I noticed that the preceding value is 0x06, which happens to be the number of bytes making up the MAC address. Length fields aren’t uncommon, but I wouldn’t expect one for something that is a fixed known length.

The next step was to look at the response to a refresh:

a9 8d fd 53 7a 1c b0 de   00 00 2c 00 02 00 16 06   00 c0 4e 08 37 f5 10 00   2b 01 03 2c 04 00 4c 53
7e 2f 01 08 30 01 ff 32   01 01 2d 01 01 2e 01 2d   12 07 43 6f 6d 74 72 6f   6c 13 0c 44 65 76 69 63
65 4d 61 73 74 65 72 14   12 53 6f 63 6b 65 74 53   65 72 76 65 72 20 31 31   2e 33 34 23 00 1a 01 01
1b 01 00 36 01 01 17 04   00 00 00 00 18 04 ff ff   ff c0 19 04 0a 8d 7a 01   1c 04 0a 8d 7a 08 1d 04
ff ff ff c0 1e 04 0a 8d   7a 01 27 01 00 28 10 00   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 2a
10 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   00 29 01 00 33 10 00 00   00 00 00 00 00 00 00 00
00 00 00 00 00 00 34 01   00 35 10 00 00 00 00 00   00 00 00 00 00 00 00 00   00 00 00 37 04 34 2e 33
31

The first thing that sticks out was that the first 10 bytes are the same as in the scan/refresh message. This is presumably a common header of some kind. This is followed by the sequence number, again matching the scan message. The next bytes (assuming 16-bit values) are 0x0002 instead of 0x000c, which could be evidence that this is a function ID/message type. This is followed by the MAC address of the sending device and then we reach new territory.

Looking for some recognisable values, I noticed that 0xff 0xff 0xff 0xc0 appears twice. The subnet mask for the network is 255.255.255.192 so I marked this as being that value. Why it appeared twice I didn’t yet know. A 0x04 appears before each one, reinforcing my earlier thoughts about length fields. Perhaps the MAC address has one because it’s following the same scheme as other fields in the messages rather than being a fixed part of the protocol. The byte prior to that (0x16 for the MAC address, 0x18 and 0x1d for the subnet masks) could be a value identifier. On this hunch I tried to split the message up based on these fields, working from the MAC address forwards. I could then use the subnet masks as a sanity check—if they came out as a field as I expected then my hunch was probably right. This gave:

a9 8d fd 53 7a 1c b0 de 00 00
2c 00
02 00
16 06 00 c0 4e 08 37 f5
10 00
2b 01 03
2c 04 00 4c 53 7e
2f 01 08
30 01 ff
32 01 01
2d 01 01
2e 01 2d
12 07 43 6f 6d 74 72 6f 6c
13 0c 44 65 76 69 63 65 4d 61 73 74 65 72
14 12 53 6f 63 6b 65 74 53 65 72 76 65 72 20 31 31 2e 33 34
23 00
1a 01 01
1b 01 00
36 01 01
17 04 00 00 00 00
18 04 ff ff ff c0
19 04 0a 8d 7a 01
1c 04 0a 8d 7a 08
1d 04 ff ff ff c0
1e 04 0a 8d 7a 01
27 01 00
28 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2a 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
29 01 00
33 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
34 01 00
35 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
37 04 34 2e 33 31

Not only did the field boundaries fall where I expected for the subnet masks, the fields neatly ended at the end of the message. I’d call that a successful hunch!

It’s worth noting that the content of a refresh response was the same as for a scan.

In the next part I’ll cover how I deciphered the meaning of these fields.