月別アーカイブ: 2020年5月

Nuro光のルーター(HG8045Q)でDMZからglobal側のIPアドレスを取得する(UPnP)

最近、自宅の回線をNuro光にしました。今までLinuxマシンにPPPoEルーターをやらせていたのですが、それと同等の環境を得るためにDMZへLinuxマシンを置いて処理をやらせるようにしました。

ここで一つ問題なのが、いかにして外部サービスに頼らすglobal IPのアドレスを調べるかです。ルーターはISPから貸与されたHG8045Qであり、管理画面は今どきのJavaScript必須なwebインターフェースです。httpで管理画面をたたいてアドレスを取得するのは面倒そうでした。できれば外部サービスには頼りたくありません。

UPnPのNAT越えについて調べてみた – いろいろな何か :こちらの記事を参考に、UPnPでアドレスを取得する方法を行いました。

まずは記事に習ってM-SEARCHを投げます。Python3用コードに書き直しました。

#!/usr/bin/python3

import socket

M_SEARCH = 'M-SEARCH * HTTP/1.1\r\n'
M_SEARCH += 'MX: 3\r\n'
M_SEARCH += 'HOST: 239.255.255.250:1900\r\n'
M_SEARCH += 'MAN: "ssdp:discover"\r\n'
M_SEARCH += 'ST: upnp:rootdevice\r\n'
M_SEARCH += '\r\n'
M_SEARCH = bytes(M_SEARCH, 'ascii')
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.settimeout(5) # 5秒でタイムアウト
s.bind(('', 1900))
# M-SEARCHをマルチキャストする
s.sendto(M_SEARCH, ('239.255.255.250', 1900))
while True:
try:
response, address = s.recvfrom(8192)
print('from', address)
       print(response.decode('ascii'))
print('=' * 40)
except socket.timeout as e: # タイムアウトしたときの処理
print(e)
break
s.close()

これを実行するとDLNAサーバになれるWindows10マシンたちも反応するのですが、必要なルータ(手で固定IPとして設定した)のレスポンスのみに着目します。

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
DATE: Thu, 14 May 2020 22:12:07 GMT
EXT:
LOCATION: http://192.168.xx.254:49652/49652gatedesc.xml
OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS: 0405f658-xxxx-xxxx-xxxx-e6e487df1fbe
SERVER: Linux/3.10.53-HULK2, UPnP/1.0, Portable SDK for UPnP devices/1.6.18
X-User-Agent: UPnP/1.0 DLNADOC/1.50
ST: urn:schemas-upnp-org:service:Layer3Forwarding:1
USN: uuid:00e0fc37-xxxx-xxxx-xxxx-084F0AC2AED2::urn:schemas-upnp-org:service:Layer3Forwarding:1


LOCATIONに書かれているURLにアクセスしてみます。

 <?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>
<friendlyName>Huawei IGD</friendlyName>
<manufacturer>Huawei</manufacturer>
<manufacturerURL>www.huawei.com</manufacturerURL>
<modelDescription>Huawei EchoLife Series</modelDescription>
<modelName>Huawei EchoLife Series</modelName>
<modelNumber>Huawei ONT</modelNumber>
<modelURL></modelURL>
(略)
<service>
<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
<serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
<controlURL>/upnp/control/WANIPConn1</controlURL>
<eventSubURL>/upnp/control/WANIPConn1</eventSubURL>
<SCPDURL>/gateconnSCPD.xml</SCPDURL>
</service>
(以下略)

参考にした記事ではWANPPPConnection:1を見るようになっていましたが、Nuro光はIPoEでアドレスをもらうためWANIPConnection:1を見るようにすれば良いようです。以下のようなコードでSOAPリクエストを投げ、結果をparseしました。

#!/usr/bin/python3
#-*- coding:utf-8 -*-

import urllib.request, urllib.error, urllib.parse
from bs4 import BeautifulSoup

HOST = '192.168.xx.254'
PORT = 49652
CONTROL = '/upnp/control/WANIPConn1'
URL = 'http://' + HOST + ':' + str(PORT) + CONTROL

SOAP = '''<?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:WANIPConnection:1">
    </m:GetExternalIPAddress>
  </s:Body>
</s:Envelope>
'''
SOAP = bytes(SOAP, 'ascii')

req = urllib.request.Request(URL, data=SOAP)
req.add_header('Content-Type', 'text/xml; charset="utf-8"')
req.add_header('SOAPACTION', '"urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress"')
#req.add_data(SOAP)

res = urllib.request.urlopen(req)

x = res.read()
x = x.decode('ascii')

soup = BeautifulSoup(x, 'xml')
ex = soup.find_all('NewExternalIPAddress')[0]
ipaddr = ex.text
print(ipaddr)

これで無事グローバスIPアドレスを外部に頼らず取得できるようになりました。これでddnsの更新が行えます。

“Adversarial Policies: Attacking Deep Reinforcement Learning”をざっと読んだ

機械学習で画像分類器をだますAdversarial Exampleは広く知られていますが、強化学習でも同じ問題があるそうです。observation対して摂動を与えることで意図しない挙動をさせることができるそうです。

observationは第三者が直接介在できるものではないのですが、環境を共有するゼロサムゲームにおいて、自身の挙動を介して相手のobservationに狙った摂動を与える、blackbox, model-freeな手法が提案されている(
https://adversarialpolicies.github.io/ )とのことでちょっと論文を読んでみました。

さすがに状況は限定されているようで、環境が高次元であること、デプロイされたエージェントの重みが固定されているという状況で、無制限に相手のポリシーがサンプリングできることが条件のようです。

見た感じ、人間同士のゲームプレイにおけるフェイントに似ているなあという印象を受けました。生成されたadversarial policyに対してfine-tuningはできるものの、それに対してさらなるadvaersarial policyを生成することができるそうで、それを交互に繰り返すことでロバストなポリシーにできるとのことです。

adversarial policyの学習は相手agentのself-playによる学習にかかる時間の3%程度でできてしまうとのことで、adversarial exampleの世界でも攻撃者有利なのは相変わらずのようです。

バーチャル背景を任意のサービスで利用する

webcamにアクセスして、人体を検出し背景画像と合成をするページ
https://knok.github.io/virtbg/ を作りました。TensorFlow.js, BodyPixを使っています。

ページそのものは単にカメラへのアクセスと画像合成だけを行うので、これをオンラインミーティングサービス等で使うためには何かしらの手段で出力をカメラに見せる必要があります。Qiitaの記事として、OBS studioとプラグインを組み焦る方法を書きました。

Windows, Linux, Macそれぞれに対応するプラグインがあるので、それらのプラットフォームで利用できます。Linuxならv4l2loopbackを使います。

ブラウザ->OBS->プラグインという処理パイプラインになるので、これでも若干のオーバーヘッドがあります。Google Meetでカメラを差し替えるChrome拡張として実装した人がいるので、この方法ならオーバーヘッドはより少なく済んでいそうです。私は面倒だったのでOBSに機種依存部分を丸投げしてしまいましたが…その代わり任意のサービスに利用ができます。Jistiでも使えるので次のオンラインセミナーではこれを使うつもりです。