ホーム » コンピュータ » Network » Buffalo ルータの情報

Buffalo ルータの情報

Home Assistant を使っていたら、UPnP の機能でルータのパケット情報を取得しているらしい。ではどうやって取得するのか?

Gemini に「UPnPの機能で Buffalo のルータの通信速度などの情報を調べるには?」と聞いたら、「SSDP(Simple Service Discovery Protocol)を使用してネットワーク上のUPnP対応デバイスを検索します。これは、マルチキャストアドレス239.255.255.250のUDPポート1900にM-SEARCHリクエストを送信することで行います。」とのアドバイスがもらえた。

「取得するプログラムを提案して」と聞いたら、以下の Python コードを教えてくれた。

import socket
import time

# SSDPマルチキャストアドレスとポート
SSDP_ADDR = '239.255.255.250'
SSDP_PORT = 1900

# M-SEARCHリクエストの作成
# ST: Service Type、ここではルーター(WANIPConnection)を指定
# MX: Maximum wait time in seconds
msearch_request = '\r\n'.join([
    'M-SEARCH * HTTP/1.1',
    'HOST: {}:{}'.format(SSDP_ADDR, SSDP_PORT),
    'MAN: "ssdp:discover"',
    'ST: urn:schemas-upnp-org:service:WANIPConnection:1',
    'MX: 3',
    '',
    ''
])

# ソケットの作成
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(5)  # 応答を待機する時間(秒)

print("M-SEARCHリクエストを送信中...")

try:
    # リクエストの送信
    sock.sendto(msearch_request.encode('utf-8'), (SSDP_ADDR, SSDP_PORT))

    while True:
        try:
            # 応答の受信
            data, addr = sock.recvfrom(1024)
            print("-" * 20)
            print("デバイスを発見しました!")
            print("IPアドレス:", addr[0])
            print("ポート:", addr[1])
            print("\n応答内容:")
            print(data.decode('utf-8'))

        except socket.timeout:
            print("-" * 20)
            print("タイムアウトしました。すべての応答を受信したか、デバイスが見つかりませんでした。")
            break

except Exception as e:
    print("エラーが発生しました:", e)

finally:
    sock.close()

実行したら、以下の情報が得られた。これより、/getedesc.xml などで 49355 ポートに問い合わせすればいいみたい。

M-SEARCHリクエストを送信中...
--------------------
デバイスを発見しました!
IPアドレス: 192.168.xx.1
ポート: 1900

応答内容:
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
DATE: Tue, 09 Sep 2025 13:57:21 GMT
EXT:
LOCATION: http://192.168.xx.1:49355/gatedesc.xml
SERVER: Linux/3.14.77 UPnP/1.0 Intel_SDK_for_UPnP_devices/1.2
ST: urn:schemas-upnp-org:service:WANIPConnection:1
USN: (略)
  • http://192.168.xx.1:49355/gatedesc.xml
  • http://192.168.xx.1:49355/gateinfoSCPD.xml – 情報量少ない
  • http://192.168.xx.1:49355/gateicfgSCPD.xml
  • http://192.168.xx.1:49355/gateconnSCPD.xml

いろいろと試す中、送受信パケットを取得する lua スクリプトを Gemini に作らせたら、下記のようになった。

-- socket.http ライブラリをロードします。
-- 'luasocket'パッケージが必要です。
local http = require("socket.http")
local ltn12 = require("ltn12")

-- UPnPサービス情報
local url = "http://192.168.xx.1:49355/upnp/control/WANCommonIFC1"

-- リクエスト情報を格納するテーブル
local requests = {
    {
        action = "GetTotalBytesReceived",
        tag = "<NewTotalBytesReceived>",
        label = "受信"
    },
    {
        action = "GetTotalBytesSent",
        tag = "<NewTotalBytesSent>",
        label = "送信"
    }
}

-- データを取得する関数
local function get_upnp_data(req_info)
    local soap_data = string.format([[<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:%s xmlns:u="urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1">
    </u:%s>
  </s:Body>
</s:Envelope>]], req_info.action, req_info.action)

    local headers = {
        ["Content-Type"] = "text/xml; charset=\"utf-8\"",
        ["SOAPAction"] = '"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1#' .. req_info.action .. '"'
    }

    print(req_info.label .. "データを取得中...")

    local response_body = {}
    local res, status_code = http.request{
        url = url,
        method = "POST",
        headers = headers,
        source = ltn12.source.string(soap_data),
        sink = ltn12.sink.table(response_body),
        timeout = 10
    }

    if status_code and (status_code == 200 or status_code == "200 OK") then
        local response_text = table.concat(response_body)

        local start_tag = req_info.tag
        local end_tag = string.gsub(start_tag, "<", "</")
        
        local _, _, bytes_string = string.find(response_text, start_tag .. "(.*)" .. end_tag)

        if bytes_string then
            local total_bytes = tonumber(bytes_string)
            local megabytes = total_bytes / 1024 / 1024
            
            print(string.format("合計%sバイト数: %d", req_info.label, total_bytes))
            print(string.format("合計%sメガバイト数: %.2f MB", req_info.label, megabytes))
        else
            print(req_info.label .. "データが見つかりませんでした。")
            print("応答内容:\n" .. response_text)
        end
    else
        print(req_info.label .. "データの取得に失敗しました。")
        print("ステータスコード: " .. (status_code or "不明"))
        print("応答内容:\n" .. table.concat(response_body))
    end
    print("----------------------------------------")
end

-- 各リクエストを実行
for _, req_info in ipairs(requests) do
    get_upnp_data(req_info)
end