fetburner.core

コアダンプ

江戸の電話を長崎で取る

 初めは音声信号に乗せたパケットを電話網で伝送してインターネットに接続していた筈が、いつしかUDPパケットに音声信号を乗せてIP網で伝送するようになり、当初は文字通り可搬式の電話だった携帯電話も情報端末として使う事の方が多くなってしまった昨今ですが、地方だと使わなくても一応固定電話の契約を残している家庭も多いのではないでしょうか?地方でなくとも、NTT系の回線だと/64のIPv6アドレスの代わりに/56のアドレスを貰う契約にすると勝手にIP電話の契約が付いてくるので、そちらを目当てに契約する人も居るかもしれません。

 うちの実家でもNTT西日本が提供するIP電話サービスであるひかり電話を契約しているのですが、折角契約しているのに使わないのは勿体無いので、僕の自宅に置いた電話機から発着信を行えるようにしてみます。

ひかり電話について

 まず、本記事で利用するIP電話サービスであるひかり電話について簡単に触れておくと、これはフレッツ光とそのコラボ回線でNTT*1が提供しているIP電話サービスです。このひかり電話を契約している場合にはVoIPゲートウェイの機能を持ったホームゲートウェイ(いわゆるひかり電話ルータ)がNTTから貸与されますから、ユーザとしてはどのように電話が繋がるかを意識せずとも、NTTの局舎から伸びていたメタル線の代わりにひかり電話ルータのモジュラージャックへ手持ちの電話機を繋ぐだけで固定電話サービスを享受できることでしょう。*2

flets.com

 実のところNTTから貸与されるひかり電話ルータは、単なるVoIPゲートウェイに留まらず簡易的なIP-PBXとしての機能も有しており、モジュラージャックに接続したアナログ電話機やSIPで登録したIP電話機から外線への発着信を行ったり、ひかり電話ルータから認識されている電話機同士で内線通話を行うこともできます。

この、モジュラーケーブルで物理的に接続したアナログ電話機のみならずIPネットワーク経由で疎通が取れたIP電話機からも発着信を行える点が肝で、極端な話VPN等で安全に疎通できているのであれば、地方の実家に置いたひかり電話ルータに東京の自宅のIP電話機を登録して発着信を行ったりもできる訳です。

電話網の構築

 それでは、実際に実家のひかり電話ルータへ自宅のIP電話機を登録し、外線の発着信を行えるような電話網を構築していきましょう。以前実家と自宅の間でVPNを張ってあるので、本質的にはLAN内の電話機を登録する際の手順と同様です。

電話機の調達

 まずSIPクライアント機能を持ったIP電話機を調達しましょう。VoIPアダプタにアナログの電話機を繋いでも良いのですが、実家にNVR500の在庫を抱えている状況でVoIPアダプタを新しく買い足すのも癪*3だったので、状態の良さそうだったCiscoIP電話機をヤフオクで購入しました。

後から気付いたのですが、ひかり電話ルータを使って内線電話網を構築したいだけならCisco IP Phone 8800シリーズを中古で買うのはあまり良い選択ではありませんでした。ざっと考え付くだけでもこれだけの懸念点があります。

幸い僕が購入した電話機にはSIPクライアント機能に対応した比較的新しいバージョンのファームウェアが入っていたのですが、残念ながらひかり電話ルータとの相性が悪く端末として登録できませんでした。風の噂ではファームウェアのバージョンによって登録できたりできなかったりするらしいですが、他メーカの製品と組み合わせると運用に支障が出るのは嫌なネットワーク機器あるあるですね。

Asterisk の設定

 ひかり電話ルータにCisco IP Phoneを直接接続する構成は相性の問題から失敗したのですが、別の電話機を買うのも勿体ないので*4コンコルドの誤謬的ではありますが相互運用性に優れたIP-PBXに両者を仲介させる事で解決を図ります。オープンソースIP-PBXであるAsteriskを端末としてひかり電話ルータ側に登録し、Cisco IP Phoneを端末としてAsteriskに登録してよしなに呼制御する訳ですね。

 まず、記事執筆時点で最新の安定板であるAsterisk 20をmlan/asteriskのDockerイメージで導入します。ここでの注意点として、SIPの特性上NATを挟んだ通信は問題が起こりがち*5なので、bridgeを使わずにホストのネットワークへ直接Asteriskコンテナを接続しています。一応NATを避けるだけならmacvlanネットワークを使っても良いのですが、手元の環境ではMACアドレスが上手く固定できなかったのでnetwork_mode: hostとしています。

version: '3'

services:
  asterisk:
    image: mlan/asterisk
    cap_add:
      - net_admin
      - net_raw
    network_mode: host
    environment:
      - SYSLOG_LEVEL=${SYSLOG_LEVEL-4}
      - HOSTNAME=${TELE_SRV-tele}.${DOMAIN-docker.localhost}
      - PULSE_SERVER=unix:/run/pulse/socket
      - PULSE_COOKIE=/run/pulse/cookie
      - WEBSMSD_PORT=${WEBSMSD_PORT-80}
    volumes:
      - ./config:/etc/asterisk
      - tele-conf:/srv
      - /etc/localtime:/etc/localtime:ro
    restart: always

volumes:
  tele-conf:

 次に、ひかり電話ルータに端末としてAsteriskを登録しましょう。どうやら僕が貸与されたひかり電話ルータはMACアドレスで認証を行っている様なので、設定画面*6からAsteriskを起動したサーバのMACアドレスを登録しています。MACアドレスだけの認証では不用心なので、ダイジェスト認証を設定するのも良いかもしれないですね。

Asterisk側ではpjsip.confを編集し、先程ひかり電話ルータ側で確認した内線番号や認証情報に基づいて設定を行いましょう。Asteriskでのひかり電話の運用に関してはVoIP-Info.jpが良く纏まっているので、これを参考にしています。

[simpletrans]
type=transport
protocol=udp
bind = 0.0.0.0:5060

[hikari-phone]
type=registration
transport=simpletrans
outbound_auth=hikari-trunk
server_uri=sip:192.168.1.1
client_uri=sip:3@192.168.1.1
retry_interval=60

[hikari-trunk]
type=auth
auth_type=userpass
password=********
username=0003

[hikari-trunk]
type=aor
contact=sip:192.168.1.1

[hikari-trunk]
type=endpoint
transport=simpletrans
context=from-hikari
disallow=all
allow=ulaw
outbound_auth=hikari-trunk
aors=hikari-trunk
direct_media=no
from_user=3
from_domain=192.168.1.1
dtmf_mode=inband

[hikari-trunk]
type=identify
endpoint=hikari-trunk
match=192.168.1.1

 ちなみに、Asteriskの設定を反映させる際はDockerコンテナを再起動するのも良いのですが、CLIから設定を反映させればダウンタイムを最小限に留められます。ついでにひかり電話ルータとの疎通が成功しているか確かめても良いでしょう。

$ sudo docker exec -it asterisk_asterisk_1 asterisk -vvvcr
*CLI> pjsip reload
*CLI> pjsip show registrations

 <Registration/ServerURI..............................>  <Auth....................>  <Status.......>
==========================================================================================

 hikari-phone/sip:192.168.1.1                           hikari-trunk                Registered        (exp. 1329s)

Objects found: 1

Cisco IP Phone の設定

 続いて、Cisco IP Phoneを端末としてAsteriskに接続する設定を行いましょう。Cisco IP Phone 8800は新しめの機種なのもあってインターネット上に転がっている情報は多くありませんが、MPPのファームウェアが入ったものを単なる電話端末として使う分にはVoIP-Info.jpで触れられている様な古いモデルの設定も割とそのまま流用できる様です。

 まず、Asterisk側にCisco IP Phoneを端末として認識するための設定を追記します。これもひかり電話の時と同様pjsip.confで設定しても良いのですが、電話機が増えてくると認証情報だけ変更して通話設定は共通にしたいモチベーションが湧いてくるでしょうから、pjsip_wizard.confで設定を共通化しておくのがお勧めです。*7

[phone-defaults](!)
type=wizard
transport = simpletrans
accepts_registrations = yes
sends_registrations = no
accepts_auth = yes
sends_auth = no
endpoint/context = default
endpoint/transport = simpletrans
endpoint/dtmf_mode = rfc4733
endpoint/disallow = all
endpoint/allow = ulaw
endpoint/allow = alaw
endpoint/rtp_symmetric = yes
endpoint/force_rport = yes
endpoint/rewrite_contact = yes
endpoint/direct_media = no
endpoint/send_pai = yes
endpoint/inband_progress = yes
endpoint/call_group = 1
endpoint/pickup_group = 1
endpoint/language = ja
endpoint/device_state_busy_at = 1
aor/max_contacts = 1
aor/qualify_frequency = 30
aor/authenticate_qualify = no

[201](phone-defaults)
inbound_auth/username = 201
inbound_auth/password = *********************

 次に、Cisco IP Phone側の設定を行いましょう。Cisco IP Phoneの設定方法は少し変わっていて、DHCPでネットワークアドレスを取得する際に150番のDHCPオプションでTFTPサーバのアドレスも取得し、そのTFTPサーバからダウンロードしたSEP(IP電話のMACアドレス).cnf.xmldialplan.xmlの内容に従って設定が行われます。

この内、SEP(IP電話のMACアドレス).cnf.xmlCisco IP Phoneが登録するIP-PBXの情報といった主要な設定を記述するのですが、何分設定項目が尋常ではなく多いので現時点では概ねVoIP-Info.jpの丸写しみたいな状態で動かしてます。これに関しては後で直す必要がありそうですね…

<device>
<deviceProtocol>SIP</deviceProtocol>
<devicePool>
 <dateTimeSetting>
  <dateTemplate>Y/M/D</dateTemplate>
  <timeZone>Tokyo Standard Time</timeZone>
   <ntps>
    <ntp priority="0">
     <name>192.168.1.1</name>
     <ntpMode>Unicast</ntpMode>
    </ntp>
  </ntps>
 </dateTimeSetting>

 <callManagerGroup>
  <members>
   <member priority="0">
    <callManager>
     <ports>
      <sipPort>5060</sipPort>
     </ports>
     <processNodeName>192.168.1.3</processNodeName>
    </callManager>
   </member>
  </members>
 </callManagerGroup>
</devicePool>

<sipProfile>
 <sipProxies>
  <registerWithProxy>true</registerWithProxy>
 </sipProxies>
 <sipCallFeatures>
  <cnfJoinEnabled>true</cnfJoinEnabled>
  <localCfwdEnable>true</localCfwdEnable>
  <callForwardURI>service-uri-cfwdall</callForwardURI>
  <callPickupURI>service-uri-pickup</callPickupURI>
  <callPickupGroupURI>service-uri-gpickup</callPickupGroupURI>
  <callHoldRingback>2</callHoldRingback>
  <semiAttendedTransfer>true</semiAttendedTransfer>
  <anonymousCallBlock>2</anonymousCallBlock>
  <callerIdBlocking>2</callerIdBlocking>
  <dndControl>2</dndControl>
  <remoteCcEnable>true</remoteCcEnable>
 </sipCallFeatures>

 <sipStack>
  <remotePartyID>true</remotePartyID>
 </sipStack>
 <sipLines>
  <line button="1">
   <featureID>9</featureID>
   <featureLabel>Asterisk</featureLabel>
   <proxy>USECALLMANAGER</proxy>
   <port>5060</port>
   <name>201</name>
   <displayName>Home Telephone</displayName>
   <autoAnswer>
    <autoAnswerEnabled>2</autoAnswerEnabled>
   </autoAnswer>
   <callWaiting>1</callWaiting>
   <authName>201</authName>
   <authPassword>********************</authPassword>
   <sharedLine>false</sharedLine>
   <messagesNumber></messagesNumber>
   <ringSettingActive>5</ringSettingActive>
   <forwardCallInfoDisplay>
    <callerName>true</callerName>
    <callerNumber>true</callerNumber>
    <redirectedNumber>true</redirectedNumber>
    <dialedNumber>true</dialedNumber>
   </forwardCallInfoDisplay>
  </line>
 </sipLines>
 <enableVad>false</enableVad>
 <preferredCodec>g711ulaw</preferredCodec>
 <dialTemplate>dialplan.xml</dialTemplate>
 <kpml>1</kpml>
 <phoneLabel></phoneLabel>
 <stutterMsgWaiting>2</stutterMsgWaiting>
 <disableLocalSpeedDialConfig>true</disableLocalSpeedDialConfig>
 <dscpForAudio>184</dscpForAudio>
 <dscpVideo>136</dscpVideo>
</sipProfile>

<commonProfile>
 <phonePassword>Cisco</phonePassword>
 <callLogBlfEnabled>2</callLogBlfEnabled>
</commonProfile>

<userLocale>
 <name>Japanese_Japan</name>
 <uid>13</uid>
 <langCode>ja</langCode>
 <version>3.3(2)JPN</version>
 <winCharSet>utf-8</winCharSet>
</userLocale>

<networkLocale>Japanese_Japan</networkLocale>
<networkLocaleInfo>
 <name>Japanese_Japan</name>
</networkLocaleInfo>
<authenticationURL></authenticationURL>
<directoryURL></directoryURL>
<servicesURL></servicesURL>
<dscpForSCCPPhoneServices>0</dscpForSCCPPhoneServices>
<dscpForCm2Dvce>96</dscpForCm2Dvce>
<transportLayerProtocol>2</transportLayerProtocol>
</device>

dialplan.xmlの方にはどの様な番号が入力された際に発信を行うかの設定を記載します。とりあえず緊急番号が入力された際には即時発信して、それ以外の番号に関しては10秒くらい待って発信を行う設定にしておきましょう。

<DIALTEMPLATE>
 <TEMPLATE MATCH="1.." Timeout="0"/>
 <TEMPLATE MATCH="0*" Timeout="10"/>
</DIALTEMPLATE>

 ここまでの設定を反映すれば、AsteriskCisco IP Phoneを端末として登録できるようになります。中古で買ったネットワーク機器は前のユーザの設定が残ってたりする事もしばしばなので、上手く疎通できない時は電話機側のファクトリリセットを試しても良いかもしれませんね。

$ sudo docker exec -it asterisk_asterisk_1 asterisk -vvvcr
*CLI> pjsip reload
*CLI> pjsip show endpoints

 Endpoint:  <Endpoint/CID.....................................>  <State.....>  <Channels.>
    I/OAuth:  <AuthId/UserName...........................................................>
        Aor:  <Aor............................................>  <MaxContact>
      Contact:  <Aor/ContactUri..........................> <Hash....> <Status> <RTT(ms)..>
  Transport:  <TransportId........>  <Type>  <cos>  <tos>  <BindAddress..................>
   Identify:  <Identify/Endpoint.........................................................>
        Match:  <criteria.........................>
    Channel:  <ChannelId......................................>  <State.....>  <Time.....>
        Exten: <DialedExten...........>  CLCID: <ConnectedLineCID.......>
==========================================================================================

 Endpoint:  201                                                  Not in use    0 of 1
     InAuth:  201-iauth/201
        Aor:  201                                                1
      Contact:  201/sip:201@192.168.100.100:5060           759221c659 Avail        34.192
  Transport:  simpletrans               udp      0      0  0.0.0.0:5060

 Endpoint:  hikari-trunk                                         Not in use    0 of inf
    OutAuth:  hikari-trunk/0003
        Aor:  hikari-trunk                                       0
      Contact:  hikari-trunk/sip:192.168.1.1              06e332d89b NonQual         nan
  Transport:  simpletrans               udp      0      0  0.0.0.0:5060
   Identify:  hikari-trunk/hikari-trunk
        Match: 192.168.1.1/32


Objects found: 2

発着信の設定

 最後にextensions.confを編集し、ひかり電話からAsteriskに着信があった際はCisco IP Phoneを鳴らし、逆にCisco IP Phoneから発信を行った際はひかり電話に取り次ぐ設定を行いましょう。Asteriskの機能としては単に右から左へ取り次ぐだけでなく高度な事もできるのですが、一旦は簡単な設定で動作を確認する事を目指します。

[globals]
INTERNALS=PJSIP/201
MYNUMBER=**********

[default]

; 1XY 特番
exten => _1XX,1,NoOp()
exten => _1XX,n,Set(CALLERID(num)=${MYNUMBER})
exten => _1XX,n,Set(CALLERID(name)=${MYNUMBER})
exten => _1XX,n,Dial(PJSIP/${EXTEN}@hikari-trunk)
; 0AB0 特番 + 0800 フリーダイアル
exten => _0ZZ0.,1,NoOp()
exten => _0ZZ0.,n,Set(CALLERID(num)=${MYNUMBER})
exten => _0ZZ0.,n,Set(CALLERID(name)=${MYNUMBER})
exten => _0ZZ0.,n,Dial(PJSIP/${EXTEN}@hikari-trunk)
exten => _0800.,1,NoOp()
exten => _0800.,n,Set(CALLERID(num)=${MYNUMBER})
exten => _0800.,n,Set(CALLERID(name)=${MYNUMBER})
exten => _0800.,n,Dial(PJSIP/${EXTEN}@hikari-trunk)
; 0A0 特番 + 080/090/070 の 11 桁通常電話
exten => _0Z0.,1,NoOp()
exten => _0Z0.,n,Set(CALLERID(num)=${MYNUMBER})
exten => _0Z0.,n,Set(CALLERID(name)=${MYNUMBER})
exten => _0Z0.,n,Dial(PJSIP/${EXTEN}@hikari-trunk)
; 00XY 特番 + 001国際電話 + 届出電気通信事業者(旧第二種電気通信事業者)
exten => _00Z.,1,NoOp()
exten => _00Z.,n,Set(CALLERID(num)=${MYNUMBER})
exten => _00Z.,n,Set(CALLERID(name)=${MYNUMBER})
exten => _00Z.,n,Dial(PJSIP/${EXTEN}@hikari-trunk)
; #特番
exten => _#[7-9].,1,NoOp()
exten => _#[7-9].,n,Set(CALLERID(num)=${MYNUMBER})
exten => _#[7-9].,n,Set(CALLERID(name)=${MYNUMBER})
exten => _#[7-9].,n,Dial(PJSIP/${EXTEN}@hikari-trunk)
; 通常の電話(0発信10桁)
exten => _0ZXXXXXXXX,1,NoOp()
exten => _0ZXXXXXXXX,n,Set(CALLERID(num)=${MYNUMBER})
exten => _0ZXXXXXXXX,n,Set(CALLERID(name)=${MYNUMBER})
exten => _0ZXXXXXXXX,n,Dial(PJSIP/${EXTEN}@hikari-trunk)

[from-hikari]

exten => s,1,NoOp()
exten => s,n,Dial(${INTERNALS},60)

 ここまでの設定が完了すれば、Cisco IP Phone側からひかり電話の番号で発信して時報を聞くことも、ひかり電話の番号に架かってきた電話をCisco IP Phoneで取る事もできるようになります。お疲れさまでした!

まとめ

 本記事ではAsteriskCisco IP Phoneを用い、実家で契約しているひかり電話の番号での発着信を遠隔地にある自宅から行えるような電話網を構築しました。言われてみれば当たり前なのですが、ネットワーク技術を使えば電話線で物理的に繋がれていた印象の強い固定電話でさえ、遠く離れた地から利用できると言うのは中々興味深く感じます。皆さんも身の回りのネットワーク機器を本来の意味でハックし、企業のネットが星を被い電子や光が駆け巡る現代を謳歌してみませんか?

*1:NTT西日本NTT東日本共に提供しています

*2:もっともNTTから貸与されたホームゲートウェイ以外でひかり電話を直接収容するのは敷居が高く、NTTの適合検査を受けて合格した端末を用いる必要があるのですが(cf. 適合検査 | 公開情報 | 企業情報 | NTT東日本

*3:NVR500に黒電話を繋いでIP電話として運用するのも、それはそれでエモいのでやってみたいのですが

*4:Cisco IP Phoneを自宅で使うエモを感じたいですし

*5:一般的なNATの実装ではIPヘッダの書き換えしか行わないが、SIPのメッセージはIPヘッダのみならずペイロードにもIPアドレスを含むため

*6:デフォルトの設定だと192.168.1.1からアクセスできます

*7:どうでも良いですが設定項目のsend_paiって先輩に空目しませんか?実際の所はsend P-Asserted-Identity headerの略らしいですが