カテゴリー別アーカイブ: 開発

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の更新が行えます。

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

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

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

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

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

Debian buster上でflatpakのSteamとProtonを動かす

前々からsnap/flatpak,  wine on dockerなど色々な方法でWindows用のゲームをDebianから動かそうと時々試していたのですが、環境をそこそこ汚さずに動作できるようになりました。

flatpak + Steam

flatpakはコンテナを利用したソフトウェア配布ツールです。同様のものにsnapがありますが、flatpakはデスクトップアプリ向けらしいです。flatpak自体はaptでインストールできます。

Steamのインストール、実行は以下のようなコマンドで行います。

flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install com.valvesoftware.Steam
flatpak run com.valvesoftware.Steam

Protonの有効化

以下の記事を参考にしました。

Days of Speed Debian StretchにSteamをインストールしてSteam Play(Proton)でWindowsゲームを遊ぶ

flatpakで入れた場合i386ランタイム等も一緒に入れてくれるので、add-architectureなどをする必要はありません。単純にSteamの設定でベータに参加しProtonを有効化するだけです。

ゲームをインストール

一般的なSteamの操作でWindows向けゲームをインストールします。初回はProtonのダウンロードとインストールが行われます。ゲームによって必要なVC runtime等も必要な場合はゲームの初回起動時に行われます。

簡単に手元で動いたものを列挙しておきます

ファイルの実体が置かれる場所

~/.var/app/com.valvesoftware.Steam 以下に配置されます。mod等を入れるならここを直接いじることになるでしょう。flatpakでSteamを消してもここは残るので不要なら直接rmしましょう。

ToDo/課題

  • ジョイスティックの利用
  • Intel Graphics以外のGPUでの動作確認
  • さらなるゲームの動作確認

追記(2019/9/10)

flathubのリポジトリ追加コマンドを記載しました。

2018年のエイプリルフール

自分の観測範囲で見かけたものをまとめてみます。ほかにFLOSS関連でこんなのがあった、という話があれば追加します。

Free Software Foundation announces crowdfunding campaign to buy Facebook — Free Software Foundation

FSFがFacebookを買収するためにクラウドファンディングを始めた、というネタ。ところでFacebookはGnuPGに寄付をしています。

Bug#894551: ITP: fascism — Exhaustive exploration of Fascist theory and practice

ファシズムをDebianパッケージ化する提案です。過去にもrootkitのITPなど、エイプリルフールネタはありました。

uim 1:1.8.6+gh20161003.0.d63dadd-6

unstableのuimを更新しました。以下の2つのRC bugを修正してあります。

前者の問題は、いくつかのパッケージ統合作業を行った時に、libuim-devのドキュメントをsymlinkに変えたことがトリガで発生した問題です。postinstで以下の処理を追加しました。これにより、symlinkが必要に応じて生成されます。

dpkg-maintscript-helper dir_to_symlink \
                /usr/share/doc/libuim-dev /usr/share/doc/libuim8 1:1.8.6+gh20161003.0.d63dadd-2.1 -- "$@"

後者の問題は 、uimバイナリパッケージに/var/lib/uimのみを所属させていたのがトリガです。パッケージ統合作業の時にlibuim-dataを廃止したのですが、このパッケージのpostrmではpurge時に”rmdir –ignore-fail-on-non-empty”を呼び出して/var/lib/uimを削除しようとします。uimパッケージに所属しているのはディレクトリのみなので、ここで削除されてしまうというわけです。

こちらの対処としては、/var/lib/uim/READMEを用意してディレクトリを空にしないという方向で解決しました。同様の処置をしているパッケージは他にもあるので、対処としては間違ってないと思います。多分。

pdnsdからdnsmasqに移行

最近自宅の光回線が非常に遅い状況だったので、改善を試みました。そのついでに、ローカルのキャッシュDNSサーバーをpdnsdからdnsmasqに変更しました。

単にIPoEでnative IPv6を導入し、DS-Liteの設定をしただけなのですが、ローカルのDNSキャッシュサーバーのフォワード先をIPv6にしたかったのです。それでいてなおかつ、/etc/hostsの内容もみてくれるものを、ということでdnsmasqにしました。

apt-getでパッケージを入れて、/etc/dnsmasq.d/local.confというファイルを新規作成して、内容を以下のようにしました。

resolv-file=/etc/dnsmasq.resolv.conf
listen-address=192.168.xxx,y,127.0.0.1

localhostとローカル側のIPでlistenするようにし、フォワード先の設定が書かれたファイルを記述しているだけです。

自分が使っているISPはv4のDNSキャッシュサーバーだとAAAAフィルタがかかっているので、それをv6に変えたことでnative v6も扱えるようになりました。

一点注意が必要だったのは、apt-get install dnsmasqだけしても他のdnsサーバーパッケージはremoveされないというところです。おかげでport 53を他のサーバーが掴んだままでdnsmasqが起動できない、という状態に陥りました。

一応今はPPPoEも併用して、IPv4側のglobal addressから接続できる状態を意地できるよう、ポリシールーティングを設定しています。DS-Liteだけだとv4で繋げるすべがないので…

DS-Liteのおかげで、通信速度は17Mbps前後だったものが70Mbps程度でるようになりました。

gnu.org日本語翻訳ミーティング参加

先日、「www.gnu.orgの日本語訳について」のミーティングに参加してきました。最近のFSIJ月例会はMumbleを用いたリモート開催を増やしているので、リアルミーティングは久しぶりです。

当日の配布資料はhttp://git.savannah.gnu.org/cgit/www-ja.git/tree/doc/meeting-2017-01.txtにあります。そちらにもgnu.org翻訳作業への参加方法が書かれていますが、興味のある方はSavannahwww-jaプロジェクトのメーリングリスト(閲覧は要登録)や、gitリポジトリの参照(こちらはanonymous clone可能 git://git.savannah.gnu.org/www-ja.git)も見てみてください。

個人的な活動 – 機械翻訳

私はこの1年ろくな活動ができていなくて申し訳なかったのですが、以前から考えていた「www-jaをコーパスとして機械学習による翻訳モデルを構築する」という活動に手をつけ始めました。TensorFlowのseq2seqサンプルを若干修正してWMTの代わりに任意のテキストファイルを与えられるようにしたコードは以前作成した(https://github.com/knok/tf-seq2seq-mod)ので、あとはデータセットを作成するだけです。

まずは以下のようなPython scriptを作ってみました。Python2, polib, natto-pyで動きます。

# -*- coding: utf-8 -*-
#

import polib
import os, sys
import re

target_dir = "./www-ja"
pos = []

for root, dirs, files in os.walk(target_dir):
    for file in files:
        if file.endswith(".ja.po"):
            po = polib.pofile(os.path.join(root, file))
            pos.append(po)
#
#import pdb; pdb.set_trace()

pair = {}
for po in pos:
    for p in po:
        msgid = p.msgid
        msgstr = p.msgstr
        pair[msgid] = msgstr
#

from natto import MeCab
m = MeCab("-Owakati")

fname_in = "input.txt"
fname_out = "output.txt"

with open(fname_in, "w") as fi, open(fname_out, "w") as fo:
    for key in pair.keys():
        val = pair[key]
        p_key = m.parse(key.encode('utf-8'))
        p_text = m.parse(val.encode('utf-8'))
        if re.match(r"^[0x00-0x7f]*$", p_text):
            continue
        fi.write(p_key)
        fi.write("\n")
        fo.write(p_text)
        fo.write("\n")

以下のような感じで実行しています。

$ python translate.py --data_dir /path/to/text \
 --train_dir /path/to/save-data --size 400 \
 --en_vocab_size 30000 --fr_vocab_size 30000 \
 --num_layers 1 --batch_size 5
Creating 1 layers of 400 units. Created model with fresh parameters.
Reading training data (limit: 0).
global step 200 learning rate 0.5000 step-time 2.50 perplexity 3205.53
 trg = FSF セミナー : GPL と LGPL の 詳細 の 研究 と 分析
 hyp = 0000 0000 0000 > > > > > > > > > > > > > > > > > > > > > >
(中略)
global step 10000 learning rate 0.4950 step-time 2.37 perplexity 1.79
 trg = < a href = " / fun / jokes / quayle . html " > クエール 氏 </ a >
 hyp = < a href = " / fun / jokes / vi . html " > ネットワーク さ </ a >

残念ながら利用可能なGPUを持っていないので、CPU演算のみで処理しています。今のところDebianで深層学習フレームワークがtesting(stretch)入りしているのはCaffe, Theanoの2つだけで、TensorFlowや自分がよく使っているChainerは入っていません。まあこのあたりはupstreamの更新も頻繁なので、あえてパッケージを使わなくともvirtualenv+pipで適時入れるほうが良い気もします。

とはいえ、GPUを使って深層学習を行うということは、ほとんどの場合プロプライエタリなソフトウェアに依存することと等価なので、「gnu.orgの翻訳」ということに限ればむしろCPUのみでの処理の方が望ましいのかもしれません。

自由なコンピューティング

実際、ミーティング当日も自由なコンピューティング環境がより一層損なわれているという話がありました。gniibeさんはChromebookにLibrebootを苦労して入れているようです。

自分もここ数日いろいろとひどい目にあいました。Xbox 360のSmartGlassを使ってみようとしたのですが、Windows 8.1の場合Xbox 360で利用しているLiveアカウントとの関連付けをWindows自身のログインアカウントと紐付けないと利用できないのです。Windows 8以降、Microsoftは極力Liveアカウントでのログインをさせるよう誘導しているのですが、8.1(とおそらく8)でそれが強制されるというのは厳しく、利用を諦めました。ただ、Windows 10ではこの点は改善されているようで、SmartGlassを含むストアアプリはアプリ単位で個別に利用するLiveアカウントを設定したり、そもそもLiveアカウントの必要ないアプリは普通にローカルアカウントのまま利用できるようになっていました。なので、Windows 10マシンでSmartGlassを利用しています。

iPhone, Android端末なども実質iCloud, googleアカウントが必須に近い状態ですが、Windowsも同様の仕組みに倒しているわけで、自由からより通い方向に進んでいるなあと痛感しました。

 メンテナ不在問題

現在メンテナが不在なGNU Projectのソフトウェアについていくつか名前が挙げられました。

一つはGNU bisonです。これは割と致命的な気がしたのですが、GCCは4以降独自パーサ(gniibeさんによると、そもそもC++が自由文脈文法でないため)を採用しているとのことなので、案外問題は少ないのかもしれません。メジャーな言語処理系でもbison(yacc)必須なものがぱっと浮かびませんし。

もうひとつはGNUbikだそうです。うーん、これは重要でもないですかね。

Mini Debian Conference Japan 2016参加

2016/12/10に開催されたMini Debian Conference Japan 2016に参加してきました。場所は日本橋のサイボウズオフィス内で、LibreOffice Kaigi 2016と同時開催でした。自分も一応両方に参加するつもりで申し込みも両方にしたのですが、結果としてDebian側の話ばかり聞いていました。

その時の様子はハッシュタグ#debianjpでツイッターを検索するとある程度様子が伺えると思います。自分が参加しているセッションについてはある程度ツイートしています。せっかくなのでMomentsを使ってみようかと思ったのですが、全部のツイートが出てこない上に時系列に並んでいない、並び替えはすべて手動という使い勝手の悪いインターフェースで断念しました。

個人的には音響モデルを含めて自由ソフトウェア実装として作ったバーチャルシンガー徵音梅林(ちおんメイリン)に最もインパクトを受けました。発表は台湾の張正一(Chou Shouichi)さんでした。

発表資料の中でも触れられていましたが、クリプトン・フィーチャーの商用ソフトウェアであるVOCALOIDは作成できる内容に制限があります。初音ミクのEULA(PDF)にありますが、公序良俗に反するなどの歌詞を含めることを禁止しています。現実にはそういった内容のものが作成、公開されているのですが、規約レベルではアウトです。張さんは、ギターを燃やすパフォーマンスの写真などを交えて「音楽活動は自由であるべきだ」と主張されていました。これには強く同意します。

ソフトウェアのベースはWORLD(修正BSDライセンス)というパテントフリーなものを使っているとのことでした。音響モデルもマスコットキャラクターもCC-BY 3.0で配布しています。

残念ながら、まだフロントエンドについては実装がこなれていないようです。デモではWineでVOCALOIDのエディタを利用されていました。まだ日本で利用している例はほとんどなさそうですが、頑張って欲しいと思います。

自作辞書のブートストラップを考える

かな漢字変換や形態素解析といった処理を行うにあたって、辞書は必要不可欠な存在です。これを一から自作することを考えています。

  1. 適切な単語のリストの作成
  2. 単語に対する読み、品詞の追加
  3. 単語に対するコスト値の算出
  4. データソースはWikipediaを使う

こんな感じでおそらくはできるでしょう。時間はかかると思うので、ゆっくり考えていくつもりです。

今はまず教師なし機械学習による単語分割を使って、単語リストを作ることを試みています。これについては階層Pitman-Yor過程に基づく可変長n-gram言語モデル(VPYLM)の実装が複数あり、それを使うことである程度下処理ができます。

Wikipediaの記事はhttps://dumps.wikimedia.org/jawiki/からダウンロードできます。本文のダンプはjawiki-latest-pages-articles.xml.bz2というファイル名です。これをWikipedia ExtractorというPythonスクリプトでプレーンテキストに変換できます。変換時に警告が出ますが、それはたいてい自己参照に関するものなので、放っておいても大丈夫そうです。

さしあたって、展開ディレクトリ/AA/wiki_00を対象にlatticelmをデフォルトパラメータで実施したところ、以下のような感じになりました。


$ head -10 data/AA/wiki_00
< doc i d=" 1 "u rl ="h t tps:/ / ja. wik iped ia. o rg/wi ki?cur id= 1 " ti tl e=" W i kip edi a : アッ プ ロー ドロ グ2 004 年 4 月 ">
W i kip edi a : アッ プ ロー ドロ グ2 004 年 4 月
< u l >
< /d oc >
< doc i d=" 2 " url =" htt ps :// ja. wik iped ia. o rg/wi ki?cur id= 2 " ti tl e=" W i kip edi a : 削 除 記 録 / 過 去 ログ 2 00 2 年 12 月 ">
W i kip edi a : 削 除 記 録 / 過 去 ログ 2 00 2 年 12 月
B e l o w i s a l i s t o f t h e m o s t r e c e n t d e l e t io n s .
A l l t i m e s s h o w n a r eser v er ( U . S . P a ci fi c ) t i m e .
< /d oc >
< doc i d=" 5 " url =" htt ps :// ja. wik iped ia. o rg/wi ki ?c urid= 5 " ti tle =" ア ン パサ ンド ">

ちょっと分割されすぎですね…ハイパーパラメータはいろいろとあるので、チューニングはいろいろと必要そうではあります。他に試された人の解説によると、初期状態がすべて分割されている点が大きいとのことです。

以前の記事で紹介した「続・わかりやすいパターン認識 教師なし学習入門」には通常のPitman-Yor過程までは解説されており、VPYMLに関してはなんとなくでしか理解出来ていません。もう少し自分の理解も深めたいところです。

単語分割自体は系列ラベル問題とみなしてRecurrent Neural Networkベースで処理することも出来るそうですが、教師あり学習なのできちんとしたデータセットを作る必要があります。そこにたどり着くまでのところを、教師なし学習との組み合わせでなんとか実現したいところです。

2016年9〜10月の活動

ここ2ヶ月ぐらいの活動を記録しておきます。

Debian関係

  • Namazuのbuild-arch/build-arch-indep対応
  • ChaSen symbolsの主要arch対応
    • qemu-debootstrapの利用
    • porterboxの利用

ChaSenに関しては前回の投稿とも関係があるのですが、とりあえずは主要(公式リリース)アーキテクチャのみ、一通りlibchasen2.symbolsの更新を行いました。

恥ずかしながら今回はじめてporterboxを使いました。これまでporterboxにログインしたことはあっても結局使い方がよくわからなくて諦めたりしていたのですが、たまたまログインしたホストのmotd(ログイン時に表示されるメッセージ)にschrootの使い方へのリンクhttps://dsa.debian.org/doc/schroot/が掲載されていて、それではじめて使い方を理解した次第です。

しかし、qemu-debootstrapでどうにかなる範囲についてはそちらで対応しました。

第12回 カーネル/VM探検隊参加

qemu-debootstrapについてそれなりに理解を深めたので、その紹介という形で発表をしてきました。

 

Qiitaへの投稿

写経に関する記事のストック数が多いのが以外です。

FSIJ月例会(予定)

今月10月の28日に、いつもの場所(東京体育館)、いつもの時間(18:30〜19:30)で勉強会が開催予定です。

GnuPG 2.1のお話だそうで、期待できます。今の時点でのDebian stable(8.x/Jessie)ではまだ標準のGnuPGが1.4系なのですが、次のリリースには2.1が標準になる予定なので、大いに参考になることでしょう。