「Asterisk」カテゴリーアーカイブ

Asterisk1.2.8

Asterisk のバージョンが上がったのでパッチの動作確認と、ついでに 1.2.8 でパッチの作り直しをしました。

chan_sip-se-timer-20060602.diff

追記

着信時に refresher=uas だった場合に、次回の re-INVITE の挙動がおかしいとの指摘をうけて修正。

chan_sip-se-timer-20060603.diff

refresher=uas で着信したことがないのですが、恐らくこれで合っていると思います…。

追記2

もう一点修正の指摘を受けたので直しておきました。

chan_sip-se-timer-20060605.diff

しつこいけれど Asterisk

いいかげん Asterisk ネタを引っ張りすぎのような気がしますが、re-INVITE の処理が気になるので書き換えました。説明するのも面倒なのでパッチにします。(Version 1.2.7.1 で作ってあります)

https://zzaj.net/chan_sip-se-timer-20060422.diff

これあてて、sip.conf の該当するチャンネル設定のところに…

session-expires=300

…と記述すると、150秒ごとに re-INVITE を行うようになってます。基本的にはパッチの内容を見てわかる人だけが使って下さい。それから動作の保証はできませんし、このパッチを使ったことによる不具合・不利益にたいしても全く保証はできません。

一応簡単に全体の処理を説明しておくと、session-expires が設定されたチャンネルに発信するときにはヘッダに Session-expires:秒数;refresher=uac と Supported:timer をつけて発信し、接続が成功したら ast_sched_add 関数を用いて session-expires で指定された秒数の半分の間隔で INVITE を送信し続けます。
そして BYE の送信または受信をしたら、ast_sched_del 関数を用いて INVITE の再送のためのタイマを削除します。

本当は refresher の入れ替えや、再送間隔のネゴシエーションも必要なのでしょうけど、とりあえず必要なさそうなので先送りです。

GXP-2000 での着信

着信時に切れてしまう件ですけど、これはただのチョンボでした。発信時に Session-Expires: 300;refresher=uac と Supported: timer を“全ての発信”に付加していたので、GXP-2000 が“正しく動作”して切断されていたのです。

じゃあ Asahi ネット F に発信するときだけ付加するようにしようというわけでもう少し手を加えました。sip.conf の個別接続に session-expires というパラメータを追加し、秒数を指定させます。(0で無効)

session-expires 保存用の変数を追加
[sip_peer 構造体]
名称は任意
 int session_expires;

設定値の読み込み処理を追加
[build_peer 関数]
while ループの中に以下を追加
 else if (!strcasecmp(v->name, “session-expires”)) {
  if ((sscanf(v->value, “%d”, &peer->session_expires) !=1) || (peer->session_expires < 0)) {    ast_log(LOG_WARNING, "'%s' is not a valid Session-Expires time value at line %d. Using default.\n", v->value, v->
lineno);
   peer->session_expires = 0;
  }
 }

peer 情報を検索する関数を追加
find_peer 関数は sip_peer 構造体の name (sip.conf のカテゴリ名) をキーに peer を検索するのですが、sip_pvt 構造体が持っている peername は peer のカテゴリ名ではなく username なので、 username をキーに peer を検索する関数が必要になります。
 static struct sip_peer *find_peer_by_username(const char *peer)
 {
  struct sip_peer *p = NULL;
  ASTOBJ_CONTAINER_TRAVERSE(&peerl, !p, do {
   if (!(strcasecmp(iterator->username, peer)))
    p = ASTOBJ_REF(iterator);
  } while (0));
  return p;
 }

ヘッダ追加処理を修正
add_header を行っていた2行を修正します。
 char expstr[64];
 struct sip_peer *peer = NULL;
 peer = find_peer_by_username(p->peername);
 if (peer != NULL && peer->session_expires > 0) {
  memset(expstr, 0, sizeof(expstr));
  snprintf(expstr, sizeof(expstr), “%d;refresher=uac”, peer->session_expires);
  p->sip_reinvite_timer = 0;
  add_header(&req, “Session-Expires”, expstr);
  add_header(&req, “Supported”, “timer”);
 }

これで Asahi ネットの IP 電話 F を asterisk に収容することができました。めでたしめでたし。

続 Asahi ネット F

結局のところ 1 回 re-INVITE を送信したら、その後の通話は継続されています。おそらくひかり電話と同じ実装になっているのではないかと思われます。

つまりこの実装では re-INVITE 時に「re-INVITE 送信側の変更」というのを行うことができ、 Session-Expires と Supported を指定しないで re-INVITE を行ったために 「re-INVITE の送信側」が網 (Asahiネット側) に移ったということです。ですから一度 re-INVITE を送信した後は、re-INVITE が Asahi ネット側から送信されるようになりました。

ただ正確に対応するためには、相手側からの送信側変更要求にも対応する必要があるのですが、とりあえず通話できるようになったので手をつけていません。

で、どこを変更すればよいかということなのですが、まともに実装しているわけではないのでパッチの公開はやめておきます。プログラムを変更できる人がわかる程度に記載することにしますね。

内容は発信時にヘッダを追加するという処理と、 INVITE に成功したら (200 OK を受け取ったら) タイマ処理をセットし、タイマ処理実行前に BYE を送信または受信したらタイマ処理をキャンセルするという処理です。

変更は chan_sip.c です。

タイマ ID の追加
[sip_pvt 構造体]
名称は任意
 int sip_reinvite_timer;

タイマ処理後にタイマ ID をクリア
[transmit_reinvite_with_sdp 関数
先頭で sip_reinvite_timer を 0 でクリア
 p->sip_reinvite_timer = 0;

タイマ関連のヘッダを追加
[transmit_invite 関数]
 p->sip_reinvite_timer = 0;
 add_header(&req, “Session-Expires”, “300;refresher=uac”);
 add_header(&req, “Supported”, “timer”);

BYE 送信時のタイマキャンセル処理
[transmit_request, transmit_request_with_auth 関数]
 if (sipmethod == SIP_BYE && p->sip_reinvite_timer != 0)
  ast_sched_del(sched, p->sip_reinvite_timer);

タイマを設定
[handle_response_invite 関数]
case 200: の break の前 (秒数はおそらく300秒以内であればOK、ここでは30秒を設定)
 if (!strcasecmp(get_header(req, “x”), “300;refresher=uac”))
  p->sip_reinvite_timer = ast_sched_add(sched, 30000, transmit_reinvite_with_sdp, p);

BYE 要求時のタイマキャンセル処理
[handle_request_bye 関数]
 if (p->sip_reinvite_timer != 0)
  ast_sched_del(sched, p->sip_reinvite_timer);

ちなみに voip-info.jp で質問したけど、AsahiネットFには誰も興味ないみたいでスルーされてます。でもこの re-INVITE をちゃんと実装すれば, RT-200{KI | NE} を使わなくてもひかり電話に接続できそうな気がするのだけどどうだろう。あそこではひかり電話がホットな話題みたいですが。

AsahiネットF

さらに調査を進めると、着信で切れるのは IP 電話機の方に原因があるようでした。Asahiネット側からは150秒ごとに re-INVITE が送信されてきて、Asterisk ではそれに対して 200 OK を返しているので問題はありません。

電話機は Grandstream GXP-2000 なのですが、なぜ切れるのかはまた後日調査することにします。

さて発信ですが、chan_sip.c をもう少しいじって INVITE が成功したら 30 秒後に timer なしで re-INVITE を送信するようにしてみたところ、300 秒を超えても通話を続けることができるようになりました。

なんか Asahi ネットの F も使えそうです。

Asterisk のタイマ

発信の出来なかった Asahi ネット F ですが、ソースコード (chan_sip.c の transmit_invite) に…

add_header(&req, “Session-Expires”, “300;refresher=uac”);
add_header(&req, “Supported”, “timer”);

…の2行を追加して“発信することだけ”は出来るようになりました。もちろんタイマが実装されていないのに「タイマをサポートしてるし、300秒ごとに re-INVITE をするよ」という意味のヘッダだけを追加しているので、300秒後には切れてしまいます。

それから今まで気付かなかったのだけど、着信のときも一定時間が過ぎると切れてしまいます。

これはバージョン 1.4 になったら実装されるのかな。実装されるのなら待つんだけど。

続・Asterisk

Asterisk をインストールして、プロバイダの IP 電話と接続しようと設定していたのですが、どうしても発信ができません。発信時に認証できないのです。

プロバイダの IP 電話というのが ASAHI ネットの F 基盤なのですが、検索してみても成功例が全然見つかりません。

あきらめてプロバイダフリーな別の IP 電話を申し込もうか思案中。

Asterisk

以前からソフトウェアPBX(IP-PBX)で電話をなんとかしたいと思っていましたが、最近どうもAsteriskというのが流行っているらしいのでちょっと調べています。LinuxのディストリビューションとしてAsteriskを組み込んだAsterisk@Homeなんていうのもあるのですね。

ただ、いろいろ見ていると電話回線を IP-PBX に取り込むところのハードウェア(FXO)が国内で流通していないのがネックになりそうです。このハードウェア(FXO)は$25くらいで買える単なるWinモデムのようなのですが、輸入してもJATEの認定がないので使えません。

これの認定を通して Asterisk@Home とかで IP-PBX のハードウェアを作ってしまうのってビジネスにならないのでしょうかねぇ。スモールオフィス向けの PBX なんて需要がありそうな気がするのですけど。