androidcgirouterlegacy-code

Obtaining data from my TP-Link router programmatically


I'm trying to design an app that can communicate with my router programmatically using the same endpoints as the web interface (there's a demo on TP-Link's website). My router is a TP-Link TD-W8980, if that matters.

The format appears to be very difficult to decipher. Here is a request which obtains the data for the status part of my app. This can obtain a valid response from the router but I'm not sure why!

I'm especially confused by the #0,0,0,0,0,0#0,0,0,0,0,0] part of the response. It's the only part I haven't managed to work out but I think I recall reading it's to do with the stack?!?

[SYS_MODE#0,0,0,0,0,0#0,0,0,0,0,0]0,1
mode
[LAN_HOST_CFG#1,0,0,0,0,0#0,0,0,0,0,0]1,1
DNSServers
[WAN_DSL_INTF_CFG#1,0,0,0,0,0#0,0,0,0,0,0]2,8
upstreamCurrRate
downstreamCurrRate
upstreamMaxRate
downstreamMaxRate
upstreamNoiseMargin
downstreamNoiseMargin
upstreamAttenuation
downstreamAttenuation
[IGD_DEV_INFO#0,0,0,0,0,0#0,0,0,0,0,0]3,3
softwareVersion
hardwareVersion
upTime
[LAN_IP_INTF#0,0,0,0,0,0#0,0,0,0,0,0]4,2
IPInterfaceIPAddress
X_TPLINK_MACAddress
[LAN_HOST_ENTRY#0,0,0,0,0,0#0,0,0,0,0,0]5,4
leaseTimeRemaining
MACAddress
hostName
IPAddress
[WAN_PPP_CONN#0,0,0,0,0,0#0,0,0,0,0,0]6,4
enable
connectionStatus
externalIPAddress
DNSServers

If it helps, the names in capitals (e.g. SYS_MODE) is the name of the section. The number after the ] is a counter stating the section number (sections can be in any order). The final number following the , is the number of parameters that follow in this section.

There are also request types for each section. In the example above, the URL is http://192.168.1.1/cgi?1&1&1&1&5&5&5. As you can see the two main request types are 1 and 5.

Here is an example response from the server. As you can see, some of the sections can be returned more than once, which makes the first number of the six zeros increment each time.

[0,0,0,0,0,0]0
mode=DSL
[1,0,0,0,0,0]1
DNSServers=x.x.x.x,x.x.x.x
[1,0,0,0,0,0]2
upstreamCurrRate=928
downstreamCurrRate=3072
upstreamMaxRate=1068
downstreamMaxRate=3104
upstreamNoiseMargin=60
downstreamNoiseMargin=57
upstreamAttenuation=295
downstreamAttenuation=546
[0,0,0,0,0,0]3
softwareVersion=0.6.0 1.3 v000e.0 Build 131012 Rel.51720n
hardwareVersion=TD-W8980 v1 00000000
upTime=x
[1,1,0,0,0,0]4
IPInterfaceIPAddress=192.168.1.1
X_TPLINK_MACAddress=xx:xx:xx:xx:xx:xx
[1,0,0,0,0,0]5
leaseTimeRemaining=-1
MACAddress=xx:xx:xx:xx:xx:xx
hostName=X
IPAddress=192.168.1.2
[2,0,0,0,0,0]5
leaseTimeRemaining=-1
MACAddress=xx:xx:xx:xx:xx:xx
hostName=X
IPAddress=192.168.1.4
[3,0,0,0,0,0]5
leaseTimeRemaining=-1
MACAddress=xx:xx:xx:xx:xx:xx
hostName=X
IPAddress=192.168.1.11
[4,0,0,0,0,0]5
leaseTimeRemaining=-1
MACAddress=xx:xx:xx:xx:xx:xx
hostName=X
IPAddress=192.168.1.5
[1,2,1,0,0,0]6
enable=1
connectionStatus=Connected
externalIPAddress=x.x.x.x
DNSServers=x.x.x.x,x.x.x.x
[2,1,1,0,0,0]6
enable=0
connectionStatus=Unconfigured
externalIPAddress=0.0.0.0
DNSServers=0.0.0.0,0.0.0.0
[3,1,1,0,0,0]6
enable=0
connectionStatus=Unconfigured
externalIPAddress=0.0.0.0
DNSServers=0.0.0.0,0.0.0.0
[error]0

I would appreciate any explanation of this format and if it appears anywhere else on the web. I've never seen such a system before!


Solution

  • This API tends to vary from model to model. TP-Link changes it a lot and there is no documentation on it, as far as I'm aware. Newer versions are JSON-based and more readable, but this one is easy to come across in their old routers.

    The best you can do is try to reverse-engineer it by looking at some of the JavaScript files that consume this API in the frontend. Most of them are not minified and should even contain comments.

    From my basic understanding, these request bodies come as such:

    [WAN_PPP_CONN#1,1,1,0,0,0#0,0,0,0,0,0]0,18
    key=value
    key=value
    [WAN_IP_CONN#1,1,1,0,0,0#0,0,0,0,0,0]0,12
    key=value
    key=value
    key=value
    

    And can be interpreted as such:

    [MIB_TAG_NAME#MIB_INDEX_STACK#UNKNOWN]TAG_INDEX,NUMBER_OF_FIELDS
    key=value
    key=value
    [MIB_TAG_NAME_2#MIB_INDEX_STACK#UNKNOWN]TAG_INDEX,NUMBER_OF_FIELDS
    key=value
    key=value
    key=value
    

    Explaining these parameters as I understand them:

    MIB_TAG_NAME => Name of the config "directory". Each one has it's own parameters

    MIB_INDEX_STACK => This appears to be a stack of indexes for all indexed fields. The equipment internally possesses a MIB (Management Information Base), which itself contains nested and sometimes indexed fields (in arrays). A simple example can be WAN configuration entries, or WANDevices in TR069. Each WANDevice possesses N WANConfigurations, which can, by themselves, possess K params. This index stack is what permits traversal in the tree where these indexes fields are involved. Generally, the last number of the stack is the only one that matters, because it will be incremented as you create WANs or remote management entries for example.

    UNKNOWN => The second stack's function is unknown to me and was never needed for anything.

    TAG_INDEX => You can set the parameters for multiple directories in the same HTTP request. However, you must index the directories as you insert them. First directory in the request body is 0, second is 1, etc..

    NUMBER_OF_FIELDS => Number of params you are setting for this directory. You can generally remove them as you wish, but you must adjust this number.

    key,value => Parameters that the "directory" accepts.