初めは音声信号に乗せたパケットを電話網で伝送してインターネットに接続していた筈が、いつしかUDPパケットに音声信号を乗せてIP網で伝送するようになり、当初は文字通り可搬式の電話だった携帯電話も情報端末として使う事の方が多くなってしまった昨今ですが、地方だと使わなくても一応固定電話の契約を残している家庭も多いのではないでしょうか?地方でなくとも、NTT系の回線だと/64のIPv6アドレスの代わりに/56のアドレスを貰う契約にすると勝手にIP電話の契約が付いてくるので、そちらを目当てに契約する人も居るかもしれません。
うちの実家でもNTT西日本が提供するIP電話サービスであるひかり電話を契約しているのですが、折角契約しているのに使わないのは勿体無いので、僕の自宅に置いた電話機から発着信を行えるようにしてみます。
ひかり電話について
まず、本記事で利用するIP電話サービスであるひかり電話について簡単に触れておくと、これはフレッツ光とそのコラボ回線でNTT*1が提供しているIP電話サービスです。このひかり電話を契約している場合にはVoIPゲートウェイの機能を持ったホームゲートウェイ(いわゆるひかり電話ルータ)がNTTから貸与されますから、ユーザとしてはどのように電話が繋がるかを意識せずとも、NTTの局舎から伸びていたメタル線の代わりにひかり電話ルータのモジュラージャックへ手持ちの電話機を繋ぐだけで固定電話サービスを享受できることでしょう。*2
実のところNTTから貸与されるひかり電話ルータは、単なるVoIPゲートウェイに留まらず簡易的なIP-PBXとしての機能も有しており、モジュラージャックに接続したアナログ電話機やSIPで登録したIP電話機から外線への発着信を行ったり、ひかり電話ルータから認識されている電話機同士で内線通話を行うこともできます。
この、モジュラーケーブルで物理的に接続したアナログ電話機のみならずIPネットワーク経由で疎通が取れたIP電話機からも発着信を行える点が肝で、極端な話VPN等で安全に疎通できているのであれば、地方の実家に置いたひかり電話ルータに東京の自宅のIP電話機を登録して発着信を行ったりもできる訳です。
電話網の構築
それでは、実際に実家のひかり電話ルータへ自宅のIP電話機を登録し、外線の発着信を行えるような電話網を構築していきましょう。以前実家と自宅の間でVPNを張ってあるので、本質的にはLAN内の電話機を登録する際の手順と同様です。
電話機の調達
まずSIPクライアント機能を持ったIP電話機を調達しましょう。VoIPアダプタにアナログの電話機を繋いでも良いのですが、実家にNVR500の在庫を抱えている状況でVoIPアダプタを新しく買い足すのも癪*3だったので、状態の良さそうだったCiscoのIP電話機をヤフオクで購入しました。
Ciscoのルータやスイッチより先にIP電話に手を出す謎の人間になりつつある pic.twitter.com/c8HCXSGD4W
— . (@fetburner) 2023年7月15日
後から気付いたのですが、ひかり電話ルータを使って内線電話網を構築したいだけならCisco IP Phone 8800シリーズを中古で買うのはあまり良い選択ではありませんでした。ざっと考え付くだけでもこれだけの懸念点があります。
- サービス契約を結んでいないので最新のファームウェアが手に入らない
- 古いファームウェアには致命的な脆弱性がある
- Cisco Unified Communications Managerとの運用に特化したファームウェアと、サードパーティ製IP-PBXとの運用を考慮したファームウェアの二種類があり実質ガチャ
幸い僕が購入した電話機には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.xml
とdialplan.xml
の内容に従って設定が行われます。
この内、SEP(IP電話のMACアドレス).cnf.xml
にCisco 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>
ここまでの設定を反映すれば、AsteriskにCisco 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で取る事もできるようになります。お疲れさまでした!
AsteriskでCiscoのIP電話を収容して、ひかり電話で外線に繋げるなどした pic.twitter.com/f8nq1NNMlg
— . (@fetburner) 2024年2月25日
まとめ
本記事ではAsteriskとCisco IP Phoneを用い、実家で契約しているひかり電話の番号での発着信を遠隔地にある自宅から行えるような電話網を構築しました。言われてみれば当たり前なのですが、ネットワーク技術を使えば電話線で物理的に繋がれていた印象の強い固定電話でさえ、遠く離れた地から利用できると言うのは中々興味深く感じます。皆さんも身の回りのネットワーク機器を本来の意味でハックし、企業のネットが星を被い電子や光が駆け巡る現代を謳歌してみませんか?
*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の略らしいですが