どうなってるの?

なんだかおかしな現象がおこったのでメモしておく。

次のソースは pcap を利用して pcap_handler に送られてきたパケットを出力するコードです。

printf("%s:%d > %s:%d",
        inet_ntoa(iph->ip_src), ntohs(tcph->source),
        inet_ntoa(iph->ip_dst), ntohs(tcph->dest));

見て分かるように

送信元IPアドレス:ポート > 送信先IPアドレス:ポート

という出力を期待しているコードです。ところが実際に実行すると

送信元IPアドレス:ポート > 送信元IPアドレス:ポート

というようにアドレスが両方とも送信元になってしまうのです。ポートは正しく、送信元ポートから送信先ポートに向かっているように表示されるのでますますわからない。そこで次のように変更してみました。

printf("%s:%d > ", inet_ntoa(iph->ip_src), ntohs(tcph->source));
printf("%s:%d?n", inet_ntoa(iph->ip_dst), ntohs(tcph->dest));

すると期待通りの出力になったのです。要するに printf を 1 行で行うとおかしな値になるのに、2 行に分けると上手くいく…。

俺が悪いのか? inet_ntoa 辺りでへんなバッファリングでもしてるだろうか?ソースを探してこようっと。

# あ、ちなみに RedHat Linux 9 です。



【調べた】 一応調べました。細かい動作までは追っていませんが、怪しい点が…。どうも、inet_ntoa はどこかに IP アドレス用のバッファを保持して使いまわしているような感じがします。スレッド間でメモリを共有しようとしているのかな?

ということは printf を 1 行で表示しようとした最初の方は最初に確保した送信元 IP アドレスがメモリに残っているうちに次の要求が来たからおかしくなったって事? 

inet_ntoa はスレッドセーフではないということかな。ということで調べてみたら、やはりスレッドセーフでは無いという説明を見つけました。
http://www23.tok2.com/home/big56/BeBookJ/NetWorkKit/Old%20Kit/NetworkDB.html
http://pc3.2ch.net/tech/kako/997/997345868.html
http://docs.hp.com/ja/B2355-60104-09/rn19re160.html

これってもしかして、有名なことなのかな。最後の URL のページは細かく書いてあるから目を通しても良いかもしれない。とにかく inet_ntoa を使うときは返り値を変数に保存しないといけないということですね。

# ちなみに自分で調べたいならば inet_ntoa のソースは glibc-version.src.rpm の中の inet/inet_ntoa.c にあります。ではでは。