楽天モバイルSIMを刺した604HWをゲートウェイとして使っているのですが,切断時に自動的に再接続してくれないので微妙に困っていました.
WebUIのチェック
WebUIではステータスや接続・切断が操作できるようになっていますが,どのような通信を行っているのでしょうか.
ブラウザのデバッガを使って何を通信しているか見てみます.
調べた結果
http://192.168.128.1/api/monitoring/status
に接続状態(接続済みなど)が含まれる
http://192.168.128.1/api/dialup/dial
で接続・切断を行う
という事が分かりました.
しかし,単純にcurlで落としてきただけではエラーが出ます.そのためセッションIDをCookieに含める必要があります.
状態を取得する
とりあえず,ブラウザのデータを拝借してcurlで読み取ってみます.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
curl -i -X GET -H "Cookie: SessionID=<セッションID>" http://192.168.128.1/api/monitoring/status
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Content-Type: text/html
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval'
X-Content-Type-Options: nosniff
Connection: Keep-Alive
Content-Length: 1277
<?xml version="1.0" encoding="UTF-8"?>
<response>
<ConnectionStatus>902</ConnectionStatus>
<WifiConnectionStatus></WifiConnectionStatus>
<SignalStrength></SignalStrength>
<SignalIcon>4</SignalIcon>
<CurrentNetworkType>19</CurrentNetworkType>
<CurrentServiceDomain>3</CurrentServiceDomain>
<RoamingStatus>0</RoamingStatus>
<BatteryStatus></BatteryStatus>
<BatteryLevel></BatteryLevel>
<BatteryPercent></BatteryPercent>
<simlockStatus>0</simlockStatus>
<PrimaryDns></PrimaryDns>
<SecondaryDns></SecondaryDns>
<PrimaryIPv6Dns></PrimaryIPv6Dns>
<SecondaryIPv6Dns></SecondaryIPv6Dns>
<CurrentWifiUser></CurrentWifiUser>
<TotalWifiUser></TotalWifiUser>
<currenttotalwifiuser>0</currenttotalwifiuser>
<ServiceStatus>2</ServiceStatus>
<SimStatus>1</SimStatus>
<WifiStatus></WifiStatus>
<CurrentNetworkTypeEx>101</CurrentNetworkTypeEx>
<maxsignal>4</maxsignal>
<wifiindooronly>-1</wifiindooronly>
<wififrequence>0</wififrequence>
<classify>DataCard</classify>
<flymode>0</flymode>
<usbup>0</usbup>
<syscfgnetmode>1</syscfgnetmode>
<dfsstatus>-1</dfsstatus>
<wifiuseablestate>-1</wifiuseablestate>
<WanIPAddress></WanIPAddress>
<WanIPv6Address></WanIPv6Address>
<wifiswitchstatus>-1</wifiswitchstatus>
<batterydisplay>1</batterydisplay>
</response>
|
切断済みのレスポンスは上記の通りとなりました.
この中で,接続状態を見たい場合,重要なのはConnectionStatus
です.
状態により,以下の通りとなりました.
状態 |
コード |
接続済み |
901 |
接続中 |
900 |
切断済み |
902 |
接続・切断を操作する
http://192.168.128.1/api/dialup/dial
にPOSTで
1
|
<?xml version="1.0" encoding="UTF-8"?><request><Action>0</Action></request>
|
を送る事で切断
1
|
<?xml version="1.0" encoding="UTF-8"?><request><Action>1</Action></request>
|
を送る事で接続
することができます.
レスポンスは200 OKで
1
|
<?xml version="1.0" encoding="UTF-8"?><response>OK</response>
|
です.
しかし,単純にセッションIDを付加しただけではエラーとなります.
ここでは更にヘッダに__RequestVerificationToken
を付加する事が必要です.
セッションIDを取得する
cookieを送信せずにhttp://192.168.128.1/
をリクエストするとレスポンスに付加されます.
RequestVerificationTokenを取得する
デバッガで追いかけた所,
http://192.168.128.1/api/webserver/token
にCookieを付加してリクエストすると取得できるようです.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
curl -i -X GET -H "Cookie: SessionID=<セッションID>" http://192.168.128.1/api/webserver/token
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Content-Type: text/html
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval'
X-Content-Type-Options: nosniff
Date: Sat, 09 Jul 2022 00:15:04 GMT
Connection: Keep-Alive
Content-Length: 138
<?xml version="1.0" encoding="UTF-8"?><response><token>トークン</token></response>
|
更に用途によって(?)前半部分と後半部分が分かれているようです.
前半32文字とその後全てで分かれており,dialの際に必要なのは後半部分です.
Pythonで自動化
必要な情報は揃いましたので,Pythonにて実際のリクエストを行ってみました.
ほぼ初pythonなので変なことしてる可能性もありますが,とりあえず動いてくれています.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
from time import sleep
from urllib import request, parse
from pprint import pprint
import re
def getSession():
cookiepattern = 'SessionID=(.*?);'
get_req = request.Request('http://192.168.128.1/', method="GET")
with request.urlopen(get_req) as res:
status = res.getcode()
if status != 200:
return 'err'
cookieraw = res.getheader('Set-Cookie')
regxres = re.match(cookiepattern,cookieraw)
if regxres:
return regxres.group(1)
else:
return "err"
def getStatus(session):
header = {'Cookie': 'SessionID=' + session + ';'}
get_req = request.Request('http://192.168.128.1/api/monitoring/status',headers=header ,method="GET")
with request.urlopen(get_req) as res:
if res.getcode() != 200:
return 'err'
body = res.read().decode('utf-8')
# 切断中
if body.find('<ConnectionStatus>902</ConnectionStatus>') != -1:
return 'disconnected'
# 接続済み
if body.find('<ConnectionStatus>901</ConnectionStatus>') != -1:
return 'connected'
# 接続中
if body.find('<ConnectionStatus>900</ConnectionStatus>') != -1:
return 'connecting'
return 'unknown'
def getVerifyToken(session):
tokenpattern = '.*<token>(.*?)</token>.*'
header = {'Cookie': 'SessionID=' + session + ';'}
get_req = request.Request('http://192.168.128.1/api/webserver/token',headers=header ,method="GET")
with request.urlopen(get_req) as res:
if res.getcode() != 200:
return 'err'
body = res.read().decode('utf-8').split(' ')[2]
regxres = re.match(tokenpattern,body)
if regxres:
return regxres.group(1)
else:
return 'err'
def requestDial(session, token, mode):
post_data = ''
if mode == 'connect':
post_data = '<?xml version="1.0" encoding="UTF-8"?><request><Action>1</Action></request>'.encode('utf-8')
else:
post_data = '<?xml version="1.0" encoding="UTF-8"?><request><Action>0</Action></request>'.encode('utf-8')
header = { 'Cookie': 'SessionID=' + session + ';',
'__RequestVerificationToken': token[32:]}
post_req = request.Request('http://192.168.128.1/api/dialup/dial',data=post_data,headers=header ,method="POST")
with request.urlopen(post_req) as res:
body = res.read().decode('utf-8')
if res.getcode() != 200:
return 'err'
if(body.find('OK')):
return 'ok'
else:
return 'err'
session = getSession()
if getStatus(session) != 'disconnected':
pprint('already connected')
exit(0)
pprint(requestDial(session,getVerifyToken(session),'connect'))
sleep(5)
if getStatus(session) != 'disconnected':
pprint('dial success')
exit(0)
else:
pprint('dial fail')
exit(1)
|
成功です✌
あとはsystemd-timerで実行するようにします.
/etc/systemd/system/604hw.service
[Unit]
Description=604hw dialer
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /usr/local/bin/604hw.py
/etc/systemd/system/604hw.timer
[Unit]
Description=Run 604hw dialer
[Timer]
OnBootSec=15min
OnUnitActiveSec=30
[Install]
WantedBy=timers.target
とりあえず30秒毎に実行すれば困らないでしょう.
おわりに
朝起きたら切れてるなんて事は無くなりそうです.
おまけ
604HWのusb_modeswitch設定
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1f0e", RUN+="/usr/sbin/usb_modeswitch -v 0x12d1 -p 1f0e -J"
更におまけ(というかメモ)
W05
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1f01", RUN+="/usr/sbin/usb_modeswitch -v 0x12d1 -p 1f01 -J"