読者です 読者をやめる 読者になる 読者になる

UPnPを使ってルータの外向けIPアドレスを取得する

UPnPを使ってルータの外向けIPアドレスを取得する方法のメモ

ルータのIPアドレスを取得するには、最初にネットワーク内のルータを見つけ出す必要があります。UPnPではSSDP(Simple Service Discovery Protocol)というプロトコルを利用してネットワーク内のデバイスの探索を行います。

SSDPで探索を行うには、マルチキャストを使ってこのような感じのHTTPリクエストを投げてやります。

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
MX: 3
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1

送信先は239.255.255.250のポート1900と決まっているようです。またST:には探索する情報の種類を指定します。真面目に仕様書等を調べてはいませんが、ルータの場合はこの値で良いようです。

MX:の値は存在意義が不明なのですが、ルータ側が返信する時のwaitに利用されます。パケットを受け取ったルータは0〜MXの間でランダムな秒数waitしてから返信を行います。また複数の該当データがあった場合にも、1件ずつwaitしてから返信するようです。サンプルコード等では3秒の場合が多いので、ここではそれに合わせています。

UPnPに対応したルータが存在した場合、送信元のアドレスにHTTPのレスポンスらしきものが帰ってきます。返信はマルチキャストではなく普通のユニキャストなUDPで行われます。SSDPでの探索ではマルチキャストを送信、ユニキャストを受信するだけなので、探索側はマルチキャストのグループに参加する必要はありません。

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=120
Location: http://192.168.1.1:2600/upnp/rootdevice.xml
SERVER: IGD-HTTP/1.1 UPnP/1.0 UPnP-Device-Host/1.0
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
EXT:
USN: uuid:03707c2a-0002007a::urn:schemas-upnp-org:device:InternetGatewayDevice:1

帰ってきたデータで重要なのはLocationです。ここに書かれたアドレスからGETすることで、そのデバイスにアクセスするために必要となる情報を取得することが出来ます。実際にHTTPでGETしてみるとこんな感じの値が返ってきます。

<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
  <specVersion>
    <major>1</major>
    <minor>0</minor>
  </specVersion>
  <device>
    <deviceType>
      urn:schemas-upnp-org:device:InternetGatewayDevice:1
    </deviceType>
    ...

必要になるのはserviceTypeがurn:schemas-upnp-org:service:WANPPPConnection:1*1となっている部分です。

            <serviceList>
              <service>
                <serviceType>
                  urn:schemas-upnp-org:service:WANPPPConnection:1
                </serviceType>
                <serviceId>
                  urn:upnp-org:serviceId:WANPPPConn1
                </serviceId>
                <controlURL>/upnp/control/WANPPPConn1</controlURL>
                <eventSubURL>/upnp/event/WANPPPConn1</eventSubURL>
                <SCPDURL>/upnp/WANPPPConn1.xml</SCPDURL>
              </service>

ルータの接続先が複数ある場合は、このserviceも複数回出現するようです。この中のcontrolURLがルータとの情報取得・設定に利用するアドレスとなります。

controlURLはSOAPインターフェイスとなっています。IPアドレスを取得するには、SOAPでGetExternalIPAddressを呼び出してやります。

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
     s:encodingStyle="http://schemas.xmlsoap.org/encoding/">
  <s:Body>
    <m:GetExternalIPAddress xmlns:m="urn:schemas-upnp-org:service:WANPPPConnection:1">
    </m:GetExternalIPAddress>
  </s:Body>
</s:Envelope>

GetExternalIPAddressの結果はNewExternalIPAddressとして返ってきます。この値がルータの外向けのIPアドレスとなります。

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <m:GetExternalIPAddressResponse xmlns:m="urn:schemas-upnp-org:service:WANPPPConnection:1">
      <NewExternalIPAddress>172.16.0.1</NewExternalIPAddress>
    </m:GetExternalIPAddressResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

後はSOAPで送るメッセージを変えるだけで、IPアドレスの取得以外にもポートマッピングの操作や接続状態の取得等も可能になるようです。

*1:ルータによってはurn:schemas-upnp-org:service:WANIPConnection:1の場合もあるみたいです