概要
この投稿では、フレッツ光ネクスト(NGN)で提供されているひかり電話へ、Asteriskを直接接続(直収)する方法を紹介する。
Asteriskをひかり電話へ直接接続するための情報は、従来から存在はしたものの非常に少なく断片的で、また古くて参考にしにくい状況がある。
更には最近のAsteriskの方向として、sipモジュールからpjsipモジュールを使うことになっており、この点でも従来からの情報は参考に出来ない。
以上のことから、ONU直下のAsteriskをpjsipを使って接続する方法を通しで紹介する。
ポイント/狙い
- 2021年時点で使える具体的な手順や設定内容を通しで紹介する。
- それに至る試行錯誤も合わせて紹介して、今後の仕様変更に対応できる手がかりを残したい。
- お世話になった5ch.netや公式コミュニティに感謝する。
作戦
- まずフレッツ網から、ひかり電話に必要な各種情報をDHCPで取得する。
- 次にAsteriskをソースからインストールする。
- 最後にDHCPで取得した情報を元に、pjsip.confを設定してレジスト(&発着信)する。
前提条件
構成
NTT/NGN
|
+---+
|ONU|
+---+
|
+---+
+-----|HUB|--------+
| +---+ |
| |
(Public IP) (Public IP)
+------+ +-------------+
| | | (eth0) |
|Router| | Raspberry Pi|
| | | (eth1) |
+------+ +-------------+
(192.168.0.x) (192.168.0.x)
| |
| +---+ |
+-----|HUB|--------+
+---+
| |
+------+ +---------+
| |
+---+ +---------+
|PC | |SIP Phone|
+---+ +---------+
- ルータとRaspberry Piそれぞれで、IPv4 PPPoE接続している。特にRaspberry Piは、オンボードのNIC(eth0)がONUへ直接接続されている。
なお投稿の趣旨と直接関係ないが、Raspberry Piをルータ直下に接続しなかったのは、面倒なNAT越えを避けるためである。 - Raspberry PiのLAN側接続は、USBのNIC(eth1)を追加して実現している。
なおRaspberry Piのeth0~eth1間は、ルーティング/フォワーディングしていない。 - Raspberry Piでは、Raspbian 10(buster)が動いている。
おことわり
- 上記の構成や以後の設定がベスト/正しいとは思っていません。
材料を提供することで、某wikiや5chで議論が出来ればと思っています。 - 法律や約款は考慮に入れていません。(この場では勘弁してください....)
手順
フレッツ網から各種情報をDHCPで取得する
まずひかり電話に関する諸情報、例えばSIPサーバのIPアドレスや電話番号などは、フレッツ網からDHCPオプション(RFC3361)で取得する。昔はPPPoE接続&POSTして情報を得ていたようだが、今は不要/不可?である。おそらくBフレッツとか光プレミアム時代には必要だったのかもしれない。
DHCPオプションについては、このような試行と判断により、dhclient(ISC DHCP)を使った。
そして設定はvoip-info.jpにある設定を使わせてもらった。
cat <<'EOF' >> /etc/dhcp/dhclient.conf
option ip-sip-servers code 120 = { boolean, array of ip-address };
option vendor-class.ntt code 210 = string;
option space ntt code width 1 length width 1 hash size 7;
option ntt.mac code 201 = string;
option ntt.number code 202 = text;
option ntt.domain code 204 = domain-list;
option ntt.firmware code 210 = domain-list;
option vendor.ntt code 210 = encapsulate ntt;
interface "eth0" {
send dhcp-client-identifier = hardware;
request subnet-mask, routers, ip-sip-servers, rfc3442-classless-static-routes, vivso;
send vendor-class.ntt = concat(06, suffix(hardware, 6));
}
EOF
他にもやろうと思えばDHCPv6で諸情報が取れたりするが、今回のひかり電話直収において必須ではないので取らなかった=voip-info.jpで紹介されているdhclient.conf
でほぼ必要十分である。
このdhclientの設定で取れる西日本での情報は以下の通り。おそらく東日本でもIPアドレスとドメイン名が違う以外、同じ項目が取得できると思われる。
lease {
interface "eth0";
fixed-address 124.xxx.xxx.xxx;
option subnet-mask 255.255.255.252;
option dhcp-lease-time 14400;
option routers 124.xxx.xxx.xxx;
option dhcp-message-type 5;
option dhcp-server-identifier 124.xxx.xxx.xxx;
option dhcp-renewal-time 7200;
option ip-sip-servers true 124.xxx.xxx.1;
option dhcp-rebinding-time 10800;
option rfc3442-classless-static-routes xxx,xxx,xxx,....;
option vivso xx:xx:xx:....;
option vendor.ntt xx:xx:xx:....;
option ntt.domain "ntt-west.ne.jp.";
option ntt.firmware "www.verinfo.hgw.flets-west.jp.";
option ntt.mac xx:xx:xx:xx:xx:xx;
option ntt.number "0xxxxxxxxx";
renew 3 2021/02/xx xx:xx:xx;
rebind 3 2021/02/xx xx:xx:xx;
expire 3 2021/02/xx xx:xx:xx;
}
先ほど「ほぼ必要十分な情報が取れる」と言ったが、実は上記の通りDHCPv4ではDNSのアドレスが取得できない。DNSはDHCPv6では取れるのだが、ひかり電話に関してDNSが必要な場面は【ntt-west.ne.jp <--> 124.xxx.xxx.1】だけなので、雑ではあるが/etc/hosts
で対応することにした。(IPv6はRaspberry Piの隣のルータがv4 over v6で使っており、ルータのconfigを変えるのが面倒くさかった)
cat <<'EOF' >> /etc/hosts
124.xxx.xxx.xxx ntt-west.ne.jp
EOF
その他これは言うまでもないが、経路情報は自動的に設定されるので、手作業で追加する必要はない。
ここまでの作業により、ifconfig
とroute
の結果は以下の通りとなる。
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 124.xxx.xxx.xxx netmask 255.255.255.252 broadcast 124.xxx.xxx.xxx
ether xx:xx:xx:xx:xx:xx txqueuelen 1000 (イーサネット)
(略)
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.0.xx netmask 255.255.255.0 broadcast 192.168.0.255
ether xx:xx:xx:xx:xx:xx txqueuelen 1000 (イーサネット)
(略)
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (ローカルループバック)
(略)
ppp0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1454
inet 153.xxx.xxx.xxx netmask 255.255.255.255 destination 153.xxx.xxx.xxx
ether xx:xx:xx:xx:xx:xx txqueuelen 1000 (イーサネット)
(略)
カーネルIP経路テーブル
受信先サイト ゲートウェイ ネットマスク フラグ Metric Ref 使用数 インタフェース
default 0.0.0.0 0.0.0.0 U 0 0 0 ppp0
124.xxx.0.0 124.xxx.xxx.xxx 255.255.xxx.0 UG 0 0 0 eth0
124.xxx.xxx.xxx 0.0.0.0 255.255.255.252 U 0 0 0 eth0
124.xxx.xxx.0 124.xxx.xxx.xxx 255.255.xxx.0 UG 0 0 0 eth0
153.xxx.xxx.xxx 0.0.0.0 255.255.255.255 UH 0 0 0 ppp0
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
xxx.xxx.xxx.0 124.xxx.xxx.xxx 255.255.224.0 UG 0 0 0 eth0
当たり前だが環境が人それぞれで違うので、この通りになるという意味ではない。
ポイントは、ifconfig
においてはeth0にフレッツ網からDHCPで取得した情報(この例だと124.xxx.xxx.xxx)がついていること、そしてroute
においてはその情報に応じた経路(この例だと124.xxx.xxx.xxxがeth0を向く)設定がされていること、である。
Asteriskをソースからインストールする
後ほど説明するが、ひかり電話直収に必要なpjsip.conf
のパラメータ(disable_rport
)は、Asterisk 16のChangeLogやAsterisk 18のChangeLogによると、16.12.0以降もしくは18.0.0以降で使えるようだ。
しかし2021年2月現在、Raspberry Pi(Raspbian)のaptで入るAsteriskは16.2.1であり、当該パラメータが使えない。従って現時点では公式サイトからソースを落としてきて、入れなければならない。
まずtar.gzを落としてきて、公式ドキュメントでも言われているとおり、何も考えずに./configure
すれば必要なパッケージをあぶり出せる。
cd /usr/src
wget https://downloads.asterisk.org/pub/telephony/asterisk/asterisk-18.2.0.tar.gz
tar -zxvf asterisk-18.2.0.tar.gz
cd asterisk-18.2.0
./configure
configure
の途中で「xxxxが足りない」などと言われたら、都度それをaptで入れていった。
なぜこうしたかと言うと、現時点でひかり電話直収に必要/必須なパッケージを明らかにしたかったからだ。voip-info.jpや他のqiitaの記事で必要なパッケージがまとめられているが、それはその時入れるAsteriskのバージョンやOS(Distribution)によって異なるので、必ずしも正しいとは限らない。
今回Raspbian 10(Buster)でAsterisk 18.2.0を入れるに際しては、以下のものの不足が指摘されたので入れた。
apt install libedit-dev uuid uuid-runtime uuid-dev libjansson-dev libxml2-dev sqlite3 libsqlite3-dev
そしてAsteriskの動作としては必須ではないが、公式コミュニティや公式ドキュメントによると、Asteriskが/etc/hosts
を読むためには以下のものが必要だそうなので、これも入れた。
apt install libunbound-dev
なおconfigure
の引数について、またpjsip対応についても公式ドキュメントで書かれているとおり、Asterisk 15.0.0以降であればデフォルトで組み込まれるようだ。なので何も設定しなかった。
configure
が問題なく終わると,以下のようになる。
./configure
(略)
.$$$$$$$$$$$$$$$=..
.$7$7.. .7$$7:.
.$$:. ,$7.7
.$7. 7$$$$ .$$77
..$$. $$$$$ .$$$7
..7$ .?. $$$$$ .?. 7$$$.
$.$. .$$$7. $$$$7 .7$$$. .$$$.
.777. .$$$$$$77$$$77$$$$$7. $$$,
$$$~ .7$$$$$$$$$$$$$7. .$$$.
.$$7 .7$$$$$$$7: ?$$$.
$$$ ?7$$$$$$$$$$I .$$$7
$$$ .7$$$$$$$$$$$$$$$$ :$$$.
$$$ $$$$$$7$$$$$$$$$$$$ .$$$.
$$$ $$$ 7$$$7 .$$$ .$$$.
$$$$ $$$$7 .$$$.
7$$$7 7$$$$ 7$$$
$$$$$ $$$
$$$$7. $$ (TM)
$$$$$$$. .7$$$$$$ $$
$$$$$$$$$$$$7$$$$$$$$$.$$$$$$
$$$$$$$$$$$$$$$$.
configure: Package configured for:
configure: OS type : linux-gnueabihf
configure: Host CPU : armv7l
configure: build-cpu:vendor:os: armv7l : unknown : linux-gnueabihf :
configure: host-cpu:vendor:os: armv7l : unknown : linux-gnueabihf :
続いて公式ドキュメントの通り、make
、make install
する。
make
(略)
+--------- Asterisk Build Complete ---------+
+ Asterisk has successfully been built, and +
+ can be installed by running: +
+ +
+ make install +
+-------------------------------------------+
make install
(略)
+---- Asterisk Installation Complete -------+
+ +
+ YOU MUST READ THE SECURITY DOCUMENT +
+ +
+ Asterisk has successfully been installed. +
+ If you would like to install the sample +
+ configuration files (overwriting any +
+ existing config files), run: +
+ +
+ For generic reference documentation: +
+ make samples +
+ +
+ For a sample basic PBX: +
+ make basic-pbx +
+ +
+ +
+----------------- or ---------------------+
+ +
+ You can go ahead and install the asterisk +
+ program documentation now or later run: +
+ +
+ make progdocs +
+ +
+ **Note** This requires that you have +
+ doxygen installed on your local system +
+-------------------------------------------+
最後の仕上げとして、公式ドキュメント(Installing Initialization ScriptやInstalling Sample Files)で紹介されているスクリプトやファイルを入れたりする。
make config
make samples
make install-logrotate
systemctl daemon-reload
systemctl enable asterisk
これでAsteriskのsystemdユニット定義ファイルが作成されたり、/etc/asterisk
の中にサンプルのconfファイルが置かれたり、/etc/logrotate.d/asterisk
が仕込まれたりする。
Asterisk(pjsip.conf, extensions.conf)を設定する
sip.conf
を使ったひかり電話直収については各所で時々見かけるが、pjsip.conf
を使ったひかり電話直収を私は見つけられなかった。なので今回頑張ってpjsip.conf
を作った。
当初はsip.conf
からpjsip.conf
へ変換するスクリプト(contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
)を使えばすぐ出来ると思っていたのだが、公式ドキュメントでも言われているとおり、常に互換性のあるconfを吐けるものではないようだ。
それを念頭において、スクリプトで一旦pjsip.conf
を作成し、それを元にレジスト&発着信できるパラメータを探った。
その苦労話は後で書くとして、結果としては以下のpjsip.conf
で動いてくれた。(注:ひかり電話直収部分のみ詳解)
[global]
type = global
debug = no
[system]
type = system
disable_rport = yes ;via headerにrportが入らないよう必須
[transport-udp]
type = transport
protocol = udp
bind = 0.0.0.0
[reg_HIKARI-DENWA]
type = registration
contact_user = 0xxxxxxxxx ;電話番号(=DHCPで取得したntt.number)
transport = transport-udp
client_uri = sip:0xxxxxxxxx@ntt-west.ne.jp ;"電話番号@DHCPで取得したntt.domain"である必要がある
server_uri = sip:ntt-west.ne.jp ;DHCPで取得したntt.domain"である必要がある
[HIKARI-DENWA]
type = aor
contact = sip:124.xxx.xxx.1 ;DHCPで取得したip-sip-servers
[HIKARI-DENWA]
type = identify
endpoint = HIKARI-DENWA
match = 124.xxx.xxx.1 ;DHCPで取得したip-sip-servers
[HIKARI-DENWA]
type = endpoint
context = incoming ;extension.conf
disallow = all
allow = ulaw
rtp_symmetric = no ;via headerにrportが入らないよう必須
force_rport = no ;via headerにrportが入らないよう必須
rewrite_contact = no ;via headerにrportが入らないよう必須
direct_media = no ;RTPをAsteriskで中継するため必要
from_domain = ntt-west.ne.jp ;DHCPで取得したntt.domain"である必要がある
aors = HIKARI-DENWA
[100]
type = aor
(略)
[100]
type = auth
(略)
[100]
type = endpoint
(略)
苦労した/大事なポイント
-
disable_rport
は必須。
pjsipではこれを設定しないと、Viaヘッダにrportという文字列が入る。rportが入るとレジストと着信は出来るが、発信が出来ない。(発信しようとinviteを投げると400 Bad Request
が返ってくる) -
client_uri
とserver_uri
は、DHCPで取得したntt.domain
(この場合はntt-west.ne.jp)である必要がある。sip_to_pjsip.py
で変換するとここがIPアドレスになったりするが、それだと何を投げても400 Bad Request
が返ってくる。 - 一方で
ntt.domain
の値を設定しただけではそのドメインの名前解決が出来ず、No Response received
になる。
なので/etc/hosts
で名前解決できるようにしてある。 -
contact
とmatch
はDHCPで取得したSIPサーバのIPアドレスで良い。 -
allow
は公式ドキュメントでデフォルト値が空欄だったので、NTTの技参資(東,西)を見てG.711 ulawにした。 -
rtp_symmetric
,force_rport
,rewrite_contact
は必須。
公式ドキュメントに書かれているが、sip.conf
におけるnat=never
にあたるもので、これと先のdisable_rport
を指定することで、Viaヘッダにrportという文字列が入らない。 -
direct_media
はエンドエンドで通信するか否かの設定だと理解していて、インターネットからひかり電話(124.xxx.xxx.1)へ直接繋がるわけがないと考えて、noにした。(今改めて考えてみると必須なのだろうか....)
その他、入れなかったパラメータで気になっているものを以下のメモに残す。
-
dtmf_mode
は公式ドキュメントでデフォルトがRFC4733であると確認し、NTTの技参資(東,西)でもRFC4733に対応していると書かれていたので、設定しなかった。
なおsip.conf
ではよくdtmfmode=rfc2833
としていたが、RFC2833を改廃したものがRFC4733である。 -
timers
,timers_min_se
,timers_sess_expires
はセッションタイマに関する設定で、今のところ必要なかったので入れなかった。もし今後、意図せず通話が切れたり切れなかったりしたら、この辺を調整してみる。 -
tos
は設定しなくても動いたので、設定しなかった。QoS担保の観点では設定すべきなのかもしれないが。
次、ひかり電話直収とは直接関係ないが、extension.conf
も参考に紹介しておく。
[general]
writeprotect=no
priorityjumping=no
[globals]
USEVOICEMAIL=NO
MYNUMBER=0xxxxxxxxx
PHONEALL=PJSIP/100(略)
[default]
exten => 100,1,Dial(PJSIP/100)
exten => 100,n,Hangup
(略)
exten => _X.,1,Dial(PJSIP/${EXTEN}@HIKARI-DENWA)
exten => _X.,n,Hangup
[incoming]
exten => _X.,1,Set(free_dial="0120")
exten => _X.,2,Set(free_call="0800")
exten => _X.,3,GotoIf($["${CALLERID(num)}"="anonymous"]?99:4)
exten => _X.,4,GotoIf($[${CALLERID(num)}:${free_dial}]?99:5)
exten => _X.,5,GotoIf($[${CALLERID(num)}:${free_call}]?99:6)
exten => _X.,6,Dial(${PHONEALL})
exten => _X.,99,Answer()
exten => _X.,n,Hangup
これに間違いがないのか自信はないが、私の理解ではこれでいいと思っていて、意図通り動いているので良しとしている。
なお少し頑張ったところとしては、incoming
でフリーダイヤルを拒否している。これはひかり電話直収と直接関係ないが、以前5ch.netでヒントをもらったので、御礼として紹介しておく。
上記の通り、DHCPで取得した電話番号やIPアドレスはpjsip.conf
やextensions.conf
で直接設定されている。この場合もしDHCPで与えられる情報が変更されると、pjsip.conf
やextensions.conf
はその情報に追随できない。
この解決策として、voip-info.jpではdhclientのhookを使ってsip.conf
を修正するようにしている。
ただ/var/lib/dhcp/dhclient.leases
を観察している限り、これらDHCPの情報は今まで変わったことがなく、これからも変わらないような気がする。
なので面倒くさいというのもあり、今のところdhclient.leases
をpjsip.conf
へ反映するスクリプトは作らず放置している。
あとがき
疲れました。
誤字脱字、内容が飛んでいる、単語の揺らぎなどあればご指摘ください。