Monthly Archives: 8月 2007 - Page 2

なるべくデーモンを止めてからキャプチャ

以前、LinuxでHDDのパフォーマンスを測定するときにはシングルユーザモードで行うと余計なプロセスが起動していないので良いという話をどこかのWebサイトから得た。

シングルユーザモードは以下のコマンドか、もしくはブートローダのカーネルオプションで指定することで、入ることが可能である。
[code]
# init 1
[/code]

これはキャプチャのときにもいえることかどうか分からないが、精神衛生上、他のプロセスが少ない状態でキャプチャを行ったほうがいいだろうと考えた。そこで動作モードを変更してキャプチャを行っている。

シングルユーザモードでは外部からsshで入ることができない。init 1ではsshdまで停止してしまう。そこでrc2.dにて最低限ネットワークを生かして、他の関係のないプロセス、例えばlpd(印刷デーモン)やcronを停止することにした。

Debianの場合、init 2〜5には違いがないらしいので、rc2.dを選んだ。止めたいプロセスを
[code]
# ls /etc/rc2.d
[/code]
で探し、
[code]
# update-rc.d -f [basename] remove
[/code]
で全てのrc.d登録を削除し、
[code]
# update-rc.d -f [basename] start 20 3 4 5 . \
stop 20 0 1 2 6 .
[/code]
にて、init 2のときに[basename]が停止するように設定した。

停止するようにしたのは、xdm, xfs, nfs, lpd, xprint, cupsys, samba, netperf, cannaとネットワークと実験に関係なさそうなプロセス。

これで、
[code]
# init 2
[/code]
とするだけで、起動していたプロセスは止まり、ps -efでもカーネルもの以外が少なくなる。

実験後、GUI等を復帰させたい場合は、
[code]
# init 5
[/code]
で元に戻る。

上記の対策を施してキャプチャを行ってみたが、違いがよく分からない。必要ないかもしれない。

解析プログラムでTCPを解析してみた

TCPもやってみた。

tshark-tcp-snd070817-2257rtp.png

TCPシーケンス図。シーケンスナンバーの値が変。後ろのLostは無視。

tshark-tcp-snd070817-2257mac.png

MACの送信回数。DCCPと傾向は似ているが、多少、強引に送っている感がある。

途中でMAC層で送信せずに待ちに入っている時間がある。この原因としてMAC層損失がありそうだが、それが発生するくらい再送している状態は見られない。よってバッファオーバーフローではないかという仮説が出てくるが、TCPの内部状態とMACドライバのオーバーフロー数を今回は観察していないので分からない。

受信してファイル出力させたデータのサイズは80.6MB。再生時間までにデータ送信が間に合っていない可能性がある。

その原因としては、誤り訂正が行われるまでデータを上位に渡さないというTCPの性質が挙げられる。TCPは再送して途中が埋まるまでACKにて再送要求を行い、既に受信したデータといえども完全性を保証するまではアプリケーションには渡さない。つまり、先のデータが届いているのにも関わらず、再送を待つことで再生までに間に合わずプレイヤーで劣化として現れてしまったという仮説が考えられる。

これを証明するためには、やはり遅延を考慮した出力を行う必要がありそうだ。

解析プログラムでDCCPを解析してみた

RTP/UDPの解析と同じ手法でDCCPの解析を行った。

tshark-dccp-snd070817-2131rtp.png

送信時にキャプチャミスは見られるものの、バッファオーバーフローはほとんど見られない。DCCPによる輻輳制御が上手く働いているものと考えられる。

tshark-dccp-snd070817-2131mac.png

10回再送が発生していないあたり、伝播状況はいいみたいだ。

tshark-dccp-rcv070817-2131rtp.png

受信時にはいくつかパケットロスしている様が見られる。これはキャプチャミスか本当にロスしているのか分からない。

データサイズの比較は、以下の通り。

  1. 送信ソースが94.8MB
  2. 送信時の推定サイズが93.7MB
  3. 受信時の推定サイズが94.0MB
  4. 受信出力ファイルサイズが93.3MB

一回ずつの実験なので傾向として言うことはできないが、結果としてRTPよりDCCPの方が再生に間に合ったファイルサイズが大きいことになる。

この理由としては伝播状況が悪いときにUDPは無理して送信することによりMAC再送が多発しバッファがオーバーしてしまうのに対して、DCCPは伝播状況が悪いときには抑制し伝播状況が良いときに送信するためにMAC層の再送が少なく済み、結果として受信できたデータサイズが大きくなったと考えられる。

この仮説の正しさを証明するためには、何回か実験を繰り返すこと、TCPでも同様の傾向が確認できることが必要そうな気がする。

解析プログラムでRTP/UDPを解析してみた(2)

送信側のMACキャプチャした結果もRTP解析する。このMACキャプチャはPCに2枚無線LANカードを挿しRTP送信していない方でモニターモードにして別のアンテナでキャプチャしたものである。

tshark-snd070817-1612rtp.png

このグラフでは、かなり多くの損失が見られる。この原因はキャプチャミスが発生しているためであり、至近距離の別のアンテナにてMACキャプチャを行っても完全にキャプチャできていないことを意味する。

また20秒から25秒にかけてキャプチャミスを原因としないような大きな損失が見られる。これはMAC層レベルでRTPシーケンスが連続しない状態で送信していることを意味する。すなわちバッファオーバーフローが発生している。

結果的にバッファオーバーフローが受信時のRTP損失に対して大きな影響を与えていることは、送信側RTP解析と受信側RTP解析の傾向から見ても確かなようだ。

MAC層の再送限界を超えることによる損失は今回はほとんど発生していない。というのも前回のMAC層のグラフを見てもMAC層で発生した損失は2回程度であり、キャプチャミスを考慮しても、それほどの数ではないはずである。

他に興味深いデータとしては、データサイズで以下のように段階ごとに損失が見られた。

  1. 元データは94.8MB
  2. 送信側MAC層キャプチャでは91.8MB
  3. 受信側IP層キャプチャでは88.2MB
  4. 受信側アプリケーションが出力したmpgは86.7MB

2.と3.のデータサイズの差がある理由はMAC層損失の件から見てもよく分からない。3.と4.に差があるのは、再生時間までにデータが間に合わなかった(バッファ時間が足りなかった)からだろうか。色々と考えることが出てきた。

解析プログラムでRTP/UDPを解析してみた

無線MAC解析プログラムができて来たので、まず、RTPでテスト。送信側でMACキャプチャ(madwifi)、受信側でIPキャプチャしたものを解析プログラムを通してgnuplotでpng出力したもの。

tshark-snd070817-1612mac.png

緑のインパルスがそのMACシーケンスコントロールを送信するために費やしたMAC送信回数を示しており、赤のラインがそのMACシーケンスコントロールのRSSIを測定している。

7秒前後で2回ほどリトライリミット回数(11回)に達しているので、高確率で受信に失敗しているはずだが…

tshark-rcv070817-1612rtp.png

緑のドットが受信できたRTPパケットの総和で、赤のインパルスが損失したRTPパケット数で前回受信したRTPシーケンスナンバーと今回受信したシーケンスナンバーから算出した損失シーケンスナンバー数。このRTPパケット損失が、動画・音声の劣化として現れる。

7秒付近では特にロストしていない。矛盾。2回とも11回で送信成功したということか。

RSSIは関連性が感じられない。というか、送信側でMACキャプチャをやっているので受信側のRSSIではない。間違い。

libpcapの使い方の補足

前回の話のおさらい。

libpcapではpcap_loopやpcap_dispatchによって各フレームに対してpcap_handlerを処理させる。

pcap_handlerでは第3引数であるu_char *dataを、ネットワーク層プロトコル番号(Ethernet)->トランスポート層プロトコル番号(IP)->DCCPシーケンス番号の順にアクセスしていくことになる。

で、補足。linuxの場合だと以下のヘッダファイルをincludeすると楽。

[code]
#include
#include
#include #include [/code]

こうすると、

[code]
struct ether_header *ether_hdr =
(struct ether_header *)data;

/* htons(ether_hdr->ether_type) = ETHERTYPE_IP なら */

int index = sizeof(struct ether_header);
struct ip *ip_hdr = (struct ip *)&data[index];

/* (ip_hdr->ip_p) = IPPROTO_DCCP なら */

index += sizeof(struct ip);
struct dccp_hdr *dccp = (struct dccp_hdr *)&data[index];
[/code]

でdccp構造体まで下れる。後は、dccp->dccph_xでシーケンス番号拡張の有無を確認し、拡張されるようであれば、

[code]
index += sizeof(struct dccp_hdr);
struct dccp_hdr_ext *dccp_hdr_ext =
(struct dccp_hdr_ext *)&data[index];
[/code]

でさらに下る。あとはdccp->dccph_seqとdccp_ext->dccph_seq_lowを処理するとDCCPのシーケンス番号が出るはず(スループット測定にシーケンス番号が必要かどうか分からないけれど)。

もしかしたらip_hdr->ip_pのエンディアンを直さなきゃいけないかもしれない。

と、こんな風にヘッダファイルをincludeすると各レイヤのプロトコル処理が楽になる、という補足。前回に数行で済むとか書いたけど、それは間違いだった。

加えてipの送信元・あて先アドレス、DCCPの送信元・あて先アドレスでフィルタリングできるようにして、802.11(と、その下のprism2header)にも対応するのが、今、自分の書いているコード。

libpcapの使い方

前回のコメントでlibpcapの使い方を知りたいということだったので、簡単なlibpcapプログラムを書いた。

“test.pcap”ファイルの各フレームの受信時間と、データリンク層プロトコル番号を表示する。

  1. pcap_open_offlineでpcapファイルを開く
  2. pcap_datalinkでデータリンク層プロトコル番号を取得する
  3. pcap_loopで各フレームに対してpcap_handlerを行う

pcap_loopの第2引数は処理するフレーム数であり-1でEOFまで処理を行う(はず)。

もしdatalinkがEthernet(1)であれば、handlerのdataにはEthernetフレーム以上が入っているので、ネットワーク層プロトコル番号(Ethernet)->トランスポート層プロトコル番号(IP)->DCCPシーケンス番号の順にアクセスする。

[code]
#include
#include

typedef struct {
u_int32_t datalink;
int start_flag;
struct timeval start_ts;
} user_hdr;

void
my_handler(u_char *_user,
const struct pcap_pkthdr *pkthdr,
const u_char *data)
{
user_hdr *user = (user_hdr *)_user;

if (user->start_flag == 0)
{
user->start_ts = pkthdr->ts;
user->start_flag = 1;
}
struct timeval ts;
timersub(&pkthdr->ts,
&user->start_ts,
&ts);

printf(“%lu.”, ts.tv_sec);
printf(“%06lu “, ts.tv_usec);
printf(“%u “, user->datalink);
printf(“\n”);
}

int
main()
{
const char *fname = “test.pcap”;

char *errbuf = NULL;
pcap_t *pcap = pcap_open_offline(fname, errbuf);

user_hdr *user = (user_hdr *)malloc(sizeof(user_hdr));
user->datalink = pcap_datalink(pcap);
user->start_flag = 0;

pcap_loop(pcap,
-1,
(pcap_handler)my_handler,
(u_char *)user);

free(user);

return 0;
}
[/code]

後はTCPもしくはDCCPのシーケンス番号が欲しければ10〜20行くらい書けば表示できる。ただしDCCPの場合はext seqがあるので少し面倒。

書いた後で思ったが、かなり行数少ない。シーケンス図やスループット図を描く為ならEtherealよりお勧めだなー。

802.11MAC解析プログラム

もう4日間ずっとプログラムを書いてる。802.11MAC解析プログラム。

内輪向けの話なんで。

スクラッチから書き直し。言語はやはり漢仕様のC。実は2年前から書き直したかった。が、その頃はプログラムをそこまで書けるレベルではなかったので諦めた。最近はCがやっとわかるようになってきたし、時間を作って、書き直すことにした。

パケットキャプチャした結果の解析は、テキスト処理で解析する手法が簡単に行える。例えばEtherealやtcpdumpにプロトコルを一度解釈させてテキスト出力し、そのテキストをphpやperl等で処理する。しかしながらEtherealなどで簡易にテキスト出力できないパラメータを取り出したかったり、キャプチャした生データから1ステップでグラフ生成まで持って行きたい場合には、やはり、libpcapによるバイナリを直接編集する手法が必要となる。

今回もlibpcapを使っているので、tcpdumpもしくはetherealでキャプチャしたデータそのまま投げられる。libpcapのrubyラッパがあって最初に検討したのだが、MACとDCCPに対応しないし、対応させようとするとrubyラッパも書かんといけないので面倒。そんな事情があって、やはりC。

既存のプログラムでは、「ある時間区間のうち、(リトライビット0フレーム+リトライビット1フレーム)/リトライビット0フレーム」を再送レートとして計算していた。そのため、(送信成功1で送信失敗10)だと11/12=11になるが、時間区間が(送信失敗10で送信成功1で送信失敗10)になると21/1=21となり、あたかも1シーケンスナンバーで20回再送したような結果となってしまう問題があった。これは時間区間がある程度大きければ問題ないが、小さくした場合に問題が発生しやすい。

そこでMAC再送は時間平均ではなく、フレームの1シーケンスコントロールあたりで解析を行うようにした。ミクロな時間でのリトライアウトがはっきりし、TCPの高速再転送の原因の1つが明確になった。

またTCPだけでなく、RTP/UDPやDCCPにも対応できるように書いている。共通の指標で評価できるようにするため。

遅延やRSSI(受信電力強度)も数値化できるように書いている。リアルタイム伝送では遅延も評価したいから。RSSIはおまけ。

よく分からないが解析時間が1000分の1位になった。1秒以内に終わるようになった。

公衆無線LANを再考する

前にも書いたけれど、公衆無線LANって収益あるのだろうか。自分が使ってないのでいまいちイメージができない。空港、ホテル、カフェで需要があるだろうことは分かる。

と、文句を言うだけでは建設的ではないので、月額料金を調べてみた。

Yahoo!は会員のみ、livedoorは安いが山手線外はエリアなし、ドコモは高い(といってもイーモバイルの1/4か…)。うーん、どうなんでしょ

コモンズとしての電波ディジタル無線技術と電波政策

コモンズとしての電波ディジタル無線技術と電波政策 RIETI 経済産業研究所

読んだ。

当時は無線LANの基地局に20万円かかったんやなー、と。まぁ野外向けなので、色々と違うだろうし、B2B製品なので高いのだろうけれど。今となっては11nの標準化とそれに伴う公衆無線LANアクセスポイントのリプレース待ちか。