├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile.am ├── README ├── README.JP.md ├── README.md ├── autogen.sh ├── configure.ac ├── m4 ├── ax_check_library.m4 └── ax_pthread.m4 └── src ├── Makefile.am ├── checker.c ├── cksum.c ├── common.c ├── disassemble.c ├── filter.c ├── hashtable.c ├── hashtable.h ├── init.c ├── listener.c ├── lnklist.c ├── lnklist.h ├── session.c ├── tcpeek.c ├── tcpeek.h └── tcpeekstat /.gitignore: -------------------------------------------------------------------------------- 1 | # Program files 2 | tcpeek 3 | 4 | # Config files 5 | 6 | # Object files 7 | *.o 8 | *.ko 9 | *.obj 10 | *.elf 11 | *.dSYM 12 | 13 | # Libraries 14 | *.lib 15 | *.a 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Autotolls files 32 | .deps 33 | Makefile 34 | Makefile.in 35 | /autom4te.cache 36 | /aclocal.m4 37 | /compile 38 | /config.guess 39 | /config.h 40 | /config.h.in 41 | /config.h.in~ 42 | /config.log 43 | /config.status 44 | /config.sub 45 | /configure 46 | /depcomp 47 | /install-sh 48 | /missing 49 | /stamp-h1 50 | 51 | # Other files 52 | *.swp 53 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | addons: 4 | apt: 5 | packages: 6 | - libpcap0.8-dev 7 | 8 | os: 9 | - linux 10 | - osx 11 | 12 | compiler: 13 | - gcc 14 | - clang 15 | 16 | matrix: 17 | exclude: 18 | - os: osx 19 | compiler: gcc 20 | allow_failures: 21 | - os: osx 22 | compiler: clang 23 | 24 | script: 25 | - ./autogen.sh 26 | - ./configure 27 | - make 28 | 29 | sudo: false 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2016 KLab Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | 3 | SUBDIRS = src 4 | 5 | EXTRA_DIST = README LICENSE autogen.sh 6 | 7 | MAINTAINERCLEANFILES = Makefile.in aclocal.m4 compile config.h.in config.h.in~ config.guess config.sub configure depcomp install-sh missing 8 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /* */ 3 | /* tcpeek README */ 4 | /* */ 5 | /* Copyright (C) 2012 KLab Inc. */ 6 | /**************************************************************************/ 7 | 8 | 【はじめに】 9 | tcpeek(てぃーしーぴーく)は、TCPのセッション確立(3wayハンドシェイク)時に発生するエラーを監視・集計するネットワークモニタです。 10 | 以下のような機能があります。 11 | 12 | ・エラー検出 - 接続に失敗したTCPセッションを集計できます 13 | ・RSTにより接続拒否されたセッションをカウントします 14 | ・ICMP Unreachにより到達不能を検出したセッションをカウントします 15 | ・接続がタイムアウトしたセッションをカウントします 16 | ・再送検出 - 再送が発生したTCPセッションを集計できます 17 | ・SYNセグメントの再送が発生したセッションをカウントします 18 | ・SYN/ACKセグメントの再送が発生したセッションをカウントします 19 | ・フィルタ - フィルタを指定して個別に集計できます 20 | ・通信方向・IPアドレス・ポート番号の組合わせでフィルタを指定します 21 | ・複数のフィルタを指定できます 22 | ・※ ただしこのポートは除く。といったフィルタも指定できます 23 | ・データ出力 - 集計したデータをUNIXドメインソケット経由で出力します 24 | ・スクリプトで扱いやすいJSON形式で出力します 25 | ・gmetric経由でrrdを出力するためのスクリプト(tcpeekstat)が付属しています 26 | 27 | 【インストール方法】 28 | $ git clone git://github.com/pandax381/tcpeek.git 29 | $ cd tcpeek 30 | $ ./configure 31 | $ make 32 | $ sudo make install 33 | 34 | 【使い方】 35 | usage: tcpeek [option]... [expression]... 36 | option: 37 | -u --user=uid # 指定したuserにsetuidして動作します 38 | -i --interface=dev # インターフェース名を指定します(例:eth0) 39 | -U --socket=path # UNIXドメインソケットのパスを指定します (デフォルト:/var/run/tcpeek/tcpeek.sock) 40 | -c --checksum=[0|1|2] # チェックサムの検証モードを指定します 0=検証なし 1=IPヘッダのみ 2=IPヘッダ+TCPヘッダ(デフォルト:0) 41 | -t --timeout=sec # セッションのタイムアウト時間(デフォルト:60) 42 | -B --buffer # libpcapのバッファサイズをMB単位で指定します(デフォルト:2) 43 | -l --loglevel=LEVEL # SYSLOGレベル(デフォルト:LOG_NOTICE)※ 現状は機能していないです 44 | -q --quiet # このオプションを指定すると、リアルタイムのセッション情報出力を抑制します 45 | --promisc # このオプションを指定すると、プロミスキャスモードで動作します 46 | --icmp # このオプションを指定すると、ICMP 到達不能メッセージを解釈するようになります 47 | -h --help # ヘルプを表示して終了します 48 | -v --version # バージョンを表示して終了します 49 | expression: 50 | [filter]:dir@addr:port[:port...][,...] 51 | example) '%' is the same as wildcard '*' 52 | tcpeek -i eth0 filter:RX@%:80:443 53 | tcpeek -i eth0 filter:TX@192.168.0.0/24:% 54 | tcpeek -i eth0 filter1:RX@%:80:443 filter2:TX@192.168.0.0/24:% 55 | 56 | -i オプションでインターフェースだけ指定すればとりあえず動きます。(デフォルトで「RX:RX@*:*」と「TX:TX@*:*」のフィルタが指定されています) 57 | 58 | $ sudo ./tcpeek -i eth0 59 | 60 | expression の指定方法が少し複雑ですが、以下のように指定します。 61 | 62 | フィルタ名:通信方向(RX|TX)@IPアドレス:ポート番号 63 | 64 | フィルタは複数指定できます。 65 | 66 | filter1:RX@192.168.0.1:80 filter2:TX@192.168.0.2:80 67 | 68 | IPアドレスとポート番号は「%」でワールドカードが指定できます。 69 | 70 | filter:TX@%:% 71 | 72 | IPアドレスにはネットワークアドレスも指定できます。 73 | 74 | filter:TX@192.168.0.0/24:% 75 | 76 | ポート番号は「:」区切りで複数指定できます。 77 | 78 | filter:TX@192.168.0.1:80:443:8080 79 | 80 | IPアドレスとポート番号の組合わせは「,」区切りで複数指定できます。 81 | 82 | filter:TX@192.168.0.1:80:443:8080,192.168.0.2:80,192.168.0.3:80:8080 83 | 84 | フィルタ名を省略すると除外フィルタとなり、条件にマッチするセッションは全てのフィルタに記録されません(記述順は関係ありません) 85 | 86 | :RX@*:22 :TX@*:22 87 | 88 | ※ 複数のフィルタにマッチするセッションは、該当する全てのフィルタにて集計されます 89 | 90 | 【結果出力】 91 | tcpeekを実行すると、標準エラーへTCPセッションの情報がリアルタイムで出力されます。 92 | 93 | $ sudo ./tcpeek -i eth0 94 | listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 95 | TIME(s) | TIMESTAMP | SRC IP:PORT DST IP:PORT | RESULTS | DUP SYN DUP S/A 96 | ---------------------------------------------------------------------------------------------------------------------- 97 | 0.002 | 12-07-06 16:39:02.552 | 192.168.2.227:48967 192.168.2.202:80 | success | 0 0 98 | 0.002 | 12-07-06 16:39:02.559 | 192.168.2.227:48968 192.168.2.202:80 | success | 0 0 99 | 0.002 | 12-07-06 16:39:11.219 | 192.168.2.227:42031 192.168.2.202:443 | success | 0 0 100 | 0.002 | 12-07-06 16:39:11.273 | 192.168.2.227:48970 192.168.2.202:80 | success | 0 0 101 | 0.002 | 12-07-06 16:39:11.279 | 192.168.2.227:42033 192.168.2.202:443 | success | 0 0 102 | 0.002 | 12-07-06 16:39:11.309 | 192.168.2.227:48972 192.168.2.202:80 | success | 0 0 103 | 0.002 | 12-07-06 16:39:11.323 | 192.168.2.227:42035 192.168.2.202:443 | success | 0 0 104 | 0.001 | 12-07-06 16:39:11.354 | 192.168.2.227:42036 192.168.2.202:443 | success | 0 0 105 | 0.002 | 12-07-06 16:39:11.385 | 192.168.2.227:42037 192.168.2.202:443 | success | 0 0 106 | 0.001 | 12-07-06 16:39:36.254 | 192.168.2.228:62876 192.168.2.227:80 | failure (reject) | 0 0 107 | 0.000 | 12-07-06 16:39:38.160 | 192.168.2.228:62877 192.168.2.227:80 | failure (reject) | 0 0 108 | 0.000 | 12-07-06 16:39:44.689 | 192.168.2.227:56371 192.168.2.228:8080 | failure (reject) | 0 0 109 | 39.947 | 12-07-06 16:41:29.723 | 192.168.2.227:58376 192.168.2.207:8080 | failure (timeout) | 2 0 110 | 111 | TIME(s) TCPセッションの確立(3wayハンドシェイク)に掛かった時間(秒) 112 | TIMESTAMP TCPセッションが開始された時刻 113 | SRC IP:PORT TCPセッションの始端(クライアント)のIPアドレスとポート番号 114 | DST IP:PORT TCPセッションの終端(サーバ)のIPアドレスとポート番号 115 | RESULTS TCPセッションの確立可否 116 | DUP SYN SYNセグメントが再送された回数(再送が発生していなければ 0) 117 | DUP S/A SYN/ACKセグメントが再送された回数(再送が発生していなければ 0) 118 | 119 | 【統計出力】 120 | Ctrl+C にて、上記の統計情報を出力して終了します。 121 | 122 | ========== TCPEEK SUMMARY ========== 123 | from : 2012-07-02 16:48:33 集計開始時刻 124 | to : 2012-07-02 16:49:59 集計終了時刻 125 | time : 86.106 (sec) 集計時間(秒) 126 | ------------------------------------ 127 | RX フィルタ名 128 | Success: 0 session 3wayハンドシェイクが成功したセッション数 129 | SYN Segment Duplicate : 0 SYNセグメントの再送が発生したセッション数 130 | S/A Segment Duplicate : 0 SYN/ACKセグメントの再送が発生したセッション数 131 | Failure: 10 session 3wayハンドシェイクが失敗したセッション数 132 | Connection Timed Out : 0 接続がタイムアウトしたセッション数 133 | Connection Rejected : 10 接続が拒否されたセッション数 134 | ------------------------------------ 135 | TX 136 | Success: 783 session 137 | SYN Segment Duplicate : 0 138 | S/A Segment Duplicate : 0 139 | Failure: 0 session 140 | Connection Timed Out : 0 141 | Connection Rejected : 0 142 | ------------------------------------ 143 | http-rx 144 | Success: 0 session 145 | SYN Segment Duplicate : 0 146 | S/A Segment Duplicate : 0 147 | Failure: 10 session 148 | Connection Timed Out : 0 149 | Connection Rejected : 10 150 | ------------------------------------ 151 | http-tx 152 | Success: 767 session 153 | SYN Segment Duplicate : 0 154 | S/A Segment Duplicate : 0 155 | Failure: 0 session 156 | Connection Timed Out : 0 157 | Connection Rejected : 0 158 | ==================================== 159 | 160 | この統計情報は、後述する tcpeekstat コマンドを使うことで、tcpeekの実行中にも取得できます。 161 | 162 | 【tcpeekstat】 163 | usage: tcpeekstat [OPTION] 164 | [OPTION] 165 | -g --gmetric # exec gmetric 166 | -U --socket=path # unix domain socket (default: /var/run/tcpeek/tcpeek.sock) 167 | -v --version # version 168 | -h --help # help 169 | 170 | tcpeekstat を実行すると、動作中のtcpeekから統計情報を取得できます。 171 | 172 | $ sudo ./tcpeekstat 173 | 174 | -g オプションを付けて実行すると、gmetricコマンド経由でrrdを出力します。 175 | 176 | $ sudo ./tcpeekstat -g 177 | 178 | ※ -g オプションなしの場合は起動時からの累計、-g オプションありの場合は「前回 -g オプションつきで実行したタイミング」からの差分を出力します 179 | 180 | 【注意事項】 181 | libpcapがインストールされている必要があります(libpcapは最新版の仕様をおすすめします http://www.tcpdump.org/#latest-release) 182 | このソフトウェアを使用して発生したいかなる損害についても作者は責任を負いません。 183 | -------------------------------------------------------------------------------- /README.JP.md: -------------------------------------------------------------------------------- 1 | # tcpeek 2 | 3 | ## はじめに 4 | 5 | tcpeek(てぃーしーぴーく)は、TCPのセッション確立(3way ハンドシェイク)時に発生するエラーを監視・集計するネットワークモニタです。 6 | 7 | 以下のような機能があります。 8 | 9 | ### エラー検出 10 | 11 | 接続に失敗した TCP セッションを集計できます 12 | 13 | + RST により接続拒否されたセッションをカウントします 14 | + ICMP Unreach により到達不能を検出したセッションをカウントします 15 | + 接続がタイムアウトしたセッションをカウントします 16 | 17 | ### 再送検出 18 | 19 | 再送が発生した TCP セッションを集計できます 20 | 21 | + SYN セグメントの再送が発生したセッションをカウントします 22 | + SYN/ACK セグメントの再送が発生したセッションをカウントします 23 | 24 | ### フィルタ 25 | 26 | フィルタを指定して個別に集計できます 27 | 28 | + 通信方向・IPアドレス・ポート番号の組合わせでフィルタを指定します 29 | + 複数のフィルタを指定できます 30 | + ※ ただしこのポートは除く、といったフィルタも指定できます 31 | 32 | ### データ出力 33 | 34 | 集計したデータを UNIX ドメインソケット経由で出力します 35 | 36 | + スクリプトで扱いやすい JSON 形式で出力します 37 | + Ganglia の gmetric 経由で rrd を出力するためのスクリプト(tcpeekstat)が付属しています 38 | 39 | ## インストール方法 40 | 41 | ``` 42 | $ git clone git://github.com/pandax381/tcpeek.git 43 | $ cd tcpeek 44 | $ ./configure 45 | $ make 46 | $ sudo make install 47 | ``` 48 | 49 | ## 使い方 50 | 51 | ``` 52 | usage: tcpeek [option]... [expression]... 53 | option: 54 | -u --user=uid # 指定したuserにsetuidして動作します 55 | -i --interface=dev # インターフェース名を指定します(例:eth0) 56 | -U --socket=path # UNIXドメインソケットのパスを指定します (デフォルト:/var/run/tcpeek/tcpeek.sock) 57 | -c --checksum=[0|1|2] # チェックサムの検証モードを指定します 0=検証なし 1=IPヘッダのみ 2=IPヘッダ+TCPヘッダ(デフォルト:0) 58 | -t --timeout=sec # セッションのタイムアウト時間(デフォルト:60) 59 | -B --buffer # libpcapのバッファサイズをMB単位で指定します(デフォルト:2) 60 | -l --loglevel=LEVEL # SYSLOGレベル(デフォルト:LOG_NOTICE)※ 現状は機能していないです 61 | -q --quiet # このオプションを指定すると、リアルタイムのセッション情報出力を抑制します 62 | --promisc # このオプションを指定すると、プロミスキャスモードで動作します 63 | --icmp # このオプションを指定すると、ICMP 到達不能メッセージを解釈するようになります 64 | -h --help # ヘルプを表示して終了します 65 | -v --version # バージョンを表示して終了します 66 | expression: 67 | [filter]:dir@addr:port[:port...][,...] 68 | example) '%' is the same as wildcard '*' 69 | tcpeek -i eth0 filter:RX@%:80:443 70 | tcpeek -i eth0 filter:TX@192.168.0.0/24:% 71 | tcpeek -i eth0 filter1:RX@%:80:443 filter2:TX@192.168.0.0/24:% 72 | ``` 73 | 74 | `-i` オプションでインターフェースだけ指定すればとりあえず動きます(デフォルトで `RX:RX@*:*` と `TX:TX@*:*` のフィルタが指定されています)。 75 | 76 | ``` 77 | $ sudo ./tcpeek -i eth0 78 | ``` 79 | 80 | `expression` の指定方法が少し複雑ですが、以下のように指定します。 81 | 82 | ``` フィルタ名:通信方向(RX|TX)@IPアドレス:ポート番号 ``` 83 | 84 | + フィルタは複数指定できます。 85 | 86 | ```filter1:RX@192.168.0.1:80 filter2:TX@192.168.0.2:80``` 87 | 88 | + IPアドレスとポート番号は `%` でワールドカードが指定できます。 89 | 90 | ```filter:TX@%:%``` 91 | 92 | + IPアドレスにはネットワークアドレスも指定できます。 93 | 94 | ```filter:TX@192.168.0.0/24:%``` 95 | 96 | + ポート番号は `:` 区切りで複数指定できます。 97 | 98 | ```filter:TX@192.168.0.1:80:443:8080``` 99 | 100 | + IPアドレスとポート番号の組合わせは `,` 区切りで複数指定できます。 101 | 102 | ```filter:TX@192.168.0.1:80:443:8080,192.168.0.2:80,192.168.0.3:80:8080``` 103 | 104 | + フィルタ名を省略すると除外フィルタとなり、条件にマッチするセッションは全てのフィルタに記録されません(記述順は関係ありません)。 105 | 106 | ```:RX@*:22 :TX@*:22``` 107 | 108 | > 複数のフィルタにマッチするセッションは、該当する全てのフィルタにて集計されます 109 | 110 | ## 結果出力 111 | 112 | tcpeekを実行すると、標準エラーへTCPセッションの情報がリアルタイムで出力されます。 113 | 114 | ``` 115 | $ sudo ./tcpeek -i eth0 116 | listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 117 | 118 | TIME(s) | TIMESTAMP | SRC IP:PORT DST IP:PORT | RESULTS | DUP SYN DUP S/A 119 | ---------------------------------------------------------------------------------------------------------------------- 120 | 0.002 | 12-07-06 16:39:02.552 | 192.168.2.227:48967 192.168.2.202:80 | success | 0 0 121 | 0.002 | 12-07-06 16:39:02.559 | 192.168.2.227:48968 192.168.2.202:80 | success | 0 0 122 | 0.002 | 12-07-06 16:39:11.219 | 192.168.2.227:42031 192.168.2.202:443 | success | 0 0 123 | 0.002 | 12-07-06 16:39:11.273 | 192.168.2.227:48970 192.168.2.202:80 | success | 0 0 124 | 0.002 | 12-07-06 16:39:11.279 | 192.168.2.227:42033 192.168.2.202:443 | success | 0 0 125 | 0.002 | 12-07-06 16:39:11.309 | 192.168.2.227:48972 192.168.2.202:80 | success | 0 0 126 | 0.002 | 12-07-06 16:39:11.323 | 192.168.2.227:42035 192.168.2.202:443 | success | 0 0 127 | 0.001 | 12-07-06 16:39:11.354 | 192.168.2.227:42036 192.168.2.202:443 | success | 0 0 128 | 0.002 | 12-07-06 16:39:11.385 | 192.168.2.227:42037 192.168.2.202:443 | success | 0 0 129 | 0.001 | 12-07-06 16:39:36.254 | 192.168.2.228:62876 192.168.2.227:80 | failure (reject) | 0 0 130 | 0.000 | 12-07-06 16:39:38.160 | 192.168.2.228:62877 192.168.2.227:80 | failure (reject) | 0 0 131 | 0.000 | 12-07-06 16:39:44.689 | 192.168.2.227:56371 192.168.2.228:8080 | failure (reject) | 0 0 132 | 39.947 | 12-07-06 16:41:29.723 | 192.168.2.227:58376 192.168.2.207:8080 | failure (timeout) | 2 0 133 | ``` 134 | 135 | + TIME(s) 136 | 137 | TCPセッションの確立(3wayハンドシェイク)に掛かった時間(秒) 138 | 139 | + TIMESTAMP 140 | 141 | TCPセッションが開始された時刻 142 | 143 | + SRC IP:PORT 144 | 145 | TCPセッションの始端(クライアント)のIPアドレスとポート番号 146 | 147 | + DST IP:PORT 148 | 149 | TCPセッションの終端(サーバ)のIPアドレスとポート番号 150 | 151 | + RESULTS 152 | 153 | TCPセッションの確立可否 154 | 155 | + DUP SYN 156 | 157 | SYNセグメントが再送された回数(再送が発生していなければ 0) 158 | 159 | + DUP S/A 160 | 161 | SYN/ACKセグメントが再送された回数(再送が発生していなければ 0) 162 | 163 | ### 統計出力 164 | 165 | `Ctrl+C` にて、上記の統計情報を出力して終了します。 166 | 167 | ``` 168 | ========== TCPEEK SUMMARY ========== 169 | from : 2012-07-02 16:48:33 # 集計開始時刻 170 | to : 2012-07-02 16:49:59 # 集計終了時刻 171 | time : 86.106 (sec) # 集計時間(秒) 172 | ------------------------------------ 173 | RX # フィルタ名 174 | Success: 0 session # 3wayハンドシェイクが成功したセッション数 175 | SYN Segment Duplicate : 0 # SYNセグメントの再送が発生したセッション数 176 | S/A Segment Duplicate : 0 # SYN/ACKセグメントの再送が発生したセッション数 177 | Failure: 10 session # 3wayハンドシェイクが失敗したセッション数 178 | Connection Timed Out : 0 # 接続がタイムアウトしたセッション数 179 | Connection Rejected : 10 # 接続が拒否されたセッション数 180 | ------------------------------------ 181 | TX 182 | Success: 783 session 183 | SYN Segment Duplicate : 0 184 | S/A Segment Duplicate : 0 185 | Failure: 0 session 186 | Connection Timed Out : 0 187 | Connection Rejected : 0 188 | ------------------------------------ 189 | http-rx 190 | Success: 0 session 191 | SYN Segment Duplicate : 0 192 | S/A Segment Duplicate : 0 193 | Failure: 10 session 194 | Connection Timed Out : 0 195 | Connection Rejected : 10 196 | ------------------------------------ 197 | http-tx 198 | Success: 767 session 199 | SYN Segment Duplicate : 0 200 | S/A Segment Duplicate : 0 201 | Failure: 0 session 202 | Connection Timed Out : 0 203 | Connection Rejected : 0 204 | ==================================== 205 | ``` 206 | 207 | この統計情報は、後述する tcpeekstat コマンドを使うことで、tcpeek の実行中にも取得できます。 208 | 209 | ## tcpeekstat 210 | 211 | ``` 212 | usage: tcpeekstat [OPTION] 213 | [OPTION] 214 | -g --gmetric # exec gmetric 215 | -U --socket=path # unix domain socket (default: /var/run/tcpeek/tcpeek.sock) 216 | -v --version # version 217 | -h --help # help 218 | ``` 219 | 220 | tcpeekstat を実行すると、動作中の tcpeek から統計情報を取得できます。 221 | 222 | ``` 223 | $ sudo ./tcpeekstat 224 | ``` 225 | 226 | `-g` オプションを付けて実行すると、Ganglia の `gmetric` コマンド経由で rrd を出力します。 227 | 228 | ``` 229 | $ sudo ./tcpeekstat -g 230 | ``` 231 | 232 | > `-g` オプションなしの場合は起動時からの累計、`-g` オプションありの場合は「前回 `-g` オプションつきで実行したタイミング」からの差分を出力します 233 | 234 | ## 注意事項 235 | 236 | `libpcap` がインストールされている必要があります(libpcapは最新版の仕様をおすすめします http://www.tcpdump.org/#latest-release)。 237 | 238 | このソフトウェアを使用して発生したいかなる損害についても作者は責任を負いません。 239 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcpeek 2 | 3 | ## Introduction 4 | 5 | tcpeek is a Network Monitor that monitors and aggregates the errors that occur when a TCP session is established (3way handshake). 6 | 7 | It has the following features: 8 | 9 | ### Error detection 10 | 11 | You can aggregate TCP sessions that failed to connect 12 | 13 | + Counts Sessions rejected by RST 14 | + ICMP Unreach counts the sessions that have detected inaccessibility 15 | + The connection counts the sessions that timed out 16 | 17 | ### Resending detection 18 | 19 | You can aggregate TCP sessions where retransmissions occur 20 | 21 | + Counts sessions where the retransmission of the SYN segment occurred 22 | + SYN / ACK counts the sessions where the segment retransmission occurred 23 | 24 | ### Filter 25 | 26 | You can specify a filter to summarize individually 27 | 28 | + Direction of communication・specify the filter in the combination of IP address and port number 29 | + Multiple filters can be specified 30 | + * You can also specify a filter such as excluding this port 31 | 32 | ### Data output 33 | 34 | Outputs the aggregated data via UNIX domain socket 35 | 36 | + Output in JSON format that is easy to handle with scripts 37 | + Comes with a script (tcpeekstat) to output rrd via Ganglia gmetric 38 | 39 | ## How to install 40 | 41 | ``` 42 | $ git clone git://github.com/pandax381/tcpeek.git 43 | $ cd tcpeek 44 | $ ./configure 45 | $ make 46 | $ sudo make install 47 | ``` 48 | 49 | ## How to use 50 | 51 | ``` 52 | usage: tcpeek [option]... [expression]... 53 | option: 54 | -u --user=uid # it works setuid to the specified user 55 | -i --interface=dev # specifies the interface name (for example, eth0) 56 | -U --socket=path # UNIX specifies the path of the domain socket (default:/var/run/tcpeek/tcpeek.sock) 57 | -c --checksum=[0|1|2] # Specify the checksum verification mode 0=No verification 1 = only IP header 2 = IP header+TCP header (default: 0) 58 | -t --timeout=sec # Session timeout (default: 60) 59 | -B --buffer # specify the buffer size of libpcap in MB (default: 2) 60 | -l --loglevel=LEVEL # SYSLOG level (default: LOG_NOTICE) ※ status is not working 61 | -q --quiet # Specify this option to suppress real-time session information output 62 | --promisc # Specify this option to operate in promiscuous mode 63 | --icmp # Specify this option to interpret ICMP unreachable messages 64 | -h --help # Exit with help 65 | -v --version # Display the version and exit 66 | expression: 67 | [filter]:dir@addr:port[:port...][,...] 68 | example) '%' is the same as wildcard '*' 69 | tcpeek -i eth0 filter:RX@%:80:443 70 | tcpeek -i eth0 filter:TX@192.168.0.0/24:% 71 | tcpeek -i eth0 filter1:RX@%:80:443 filter2:TX@192.168.0.0/24:% 72 | ``` 73 | 74 | if you specify only the interface in the `-i` option, it will work anyway (by default, the filters `RX:RX@*:*` and `TX:TX@*:*` are specified). 75 | 76 | ``` 77 | $ sudo ./tcpeek -i eth0 78 | ``` 79 | 80 | `expression` It is a bit more complicated to specify, but it is specified as follows. 81 | 82 | ``` Filter name: communication direction (RX|TX)@IP address: port number ``` 83 | 84 | + Multiple filters can be specified. 85 | 86 | ```filter1:RX@192.168.0.1:80 filter2:TX@192.168.0.2:80``` 87 | 88 | + The IP address and port number are `%` and the World card can be specified. 89 | 90 | ```filter:TX@%:%``` 91 | 92 | + The IP address can also be a network address. 93 | 94 | ```filter:TX@192.168.0.0/24:%``` 95 | 96 | + You can specify multiple port numbers with a `:` separator. 97 | 98 | ```filter:TX@192.168.0.1:80:443:8080``` 99 | 100 | + You can specify more than one combination of IP address and port number separated by `,`. 101 | 102 | ```filter:TX@192.168.0.1:80:443:8080,192.168.0.2:80,192.168.0.3:80:8080``` 103 | 104 | + If you omit the filter name, it becomes an exclusion filter, the session that matches the condition will not be recorded in all filters (the order of description does not matter). 105 | 106 | ```:RX@*:22 :TX@*:22``` 107 | 108 | > Sessions that match more than one filter will be aggregated across all applicable filters 109 | 110 | ## Result output 111 | 112 | when tcpeek is executed, the information of the TCP session is output to the standard error in real time. 113 | 114 | ``` 115 | $ sudo ./tcpeek -i eth0 116 | listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 117 | 118 | TIME(s) | TIMESTAMP | SRC IP:PORT DST IP:PORT | RESULTS | DUP SYN DUP S/A 119 | ---------------------------------------------------------------------------------------------------------------------- 120 | 0.002 | 12-07-06 16:39:02.552 | 192.168.2.227:48967 192.168.2.202:80 | success | 0 0 121 | 0.002 | 12-07-06 16:39:02.559 | 192.168.2.227:48968 192.168.2.202:80 | success | 0 0 122 | 0.002 | 12-07-06 16:39:11.219 | 192.168.2.227:42031 192.168.2.202:443 | success | 0 0 123 | 0.002 | 12-07-06 16:39:11.273 | 192.168.2.227:48970 192.168.2.202:80 | success | 0 0 124 | 0.002 | 12-07-06 16:39:11.279 | 192.168.2.227:42033 192.168.2.202:443 | success | 0 0 125 | 0.002 | 12-07-06 16:39:11.309 | 192.168.2.227:48972 192.168.2.202:80 | success | 0 0 126 | 0.002 | 12-07-06 16:39:11.323 | 192.168.2.227:42035 192.168.2.202:443 | success | 0 0 127 | 0.001 | 12-07-06 16:39:11.354 | 192.168.2.227:42036 192.168.2.202:443 | success | 0 0 128 | 0.002 | 12-07-06 16:39:11.385 | 192.168.2.227:42037 192.168.2.202:443 | success | 0 0 129 | 0.001 | 12-07-06 16:39:36.254 | 192.168.2.228:62876 192.168.2.227:80 | failure (reject) | 0 0 130 | 0.000 | 12-07-06 16:39:38.160 | 192.168.2.228:62877 192.168.2.227:80 | failure (reject) | 0 0 131 | 0.000 | 12-07-06 16:39:44.689 | 192.168.2.227:56371 192.168.2.228:8080 | failure (reject) | 0 0 132 | 39.947 | 12-07-06 16:41:29.723 | 192.168.2.227:58376 192.168.2.207:8080 | failure (timeout) | 2 0 133 | ``` 134 | 135 | + TIME(s) 136 | 137 | Time (in seconds) spent on establishing a TCP session (3way handshake) 138 | 139 | + TIMESTAMP 140 | 141 | The time when the TCP session started 142 | 143 | + SRC IP:PORT 144 | 145 | IP address and port number of the beginning of the TCP session (client) 146 | 147 | + DST IP:PORT 148 | 149 | TCP session termination (server) IP address and port number 150 | 151 | + RESULTS 152 | 153 | TCP session availability 154 | 155 | + DUP SYN 156 | 157 | The number of times the SYN segment was retransmitted (0 if no retransmissions occur) 158 | 159 | + DUP S/A 160 | 161 | The number of times the SYN/ACK segment was retransmitted (0 if no retransmissions occur) 162 | 163 | ### Statistical output 164 | 165 | Output the above statistics by `Ctrl+C` and exit. 166 | 167 | ``` 168 | ========== TCPEEK SUMMARY ========== 169 | from : 2012-07-02 16:48:33 # aggregate start-time 170 | to : 2012-07-02 16:49:59 # aggregate end-time 171 | time : 86.106 (sec) # Time (seconds) 172 | ------------------------------------ 173 | RX # filter name 174 | Success: 0 session # 3way number of successful sessions 175 | SYN Segment Duplicate : 0 # the number of sessions for which the retransmission of the SYN segment occurred 176 | S/A Segment Duplicate : 0 # SYN/ACK the number of sessions in which the segment was resent 177 | Failure: 10 session # 3way number of sessions where the handshake failed 178 | Connection Timed Out : 0 # number of Sessions the connection timed out 179 | Connection Rejected : 10 # the number of sessions that the connection was denied 180 | ------------------------------------ 181 | TX 182 | Success: 783 session 183 | SYN Segment Duplicate : 0 184 | S/A Segment Duplicate : 0 185 | Failure: 0 session 186 | Connection Timed Out : 0 187 | Connection Rejected : 0 188 | ------------------------------------ 189 | http-rx 190 | Success: 0 session 191 | SYN Segment Duplicate : 0 192 | S/A Segment Duplicate : 0 193 | Failure: 10 session 194 | Connection Timed Out : 0 195 | Connection Rejected : 10 196 | ------------------------------------ 197 | http-tx 198 | Success: 767 session 199 | SYN Segment Duplicate : 0 200 | S/A Segment Duplicate : 0 201 | Failure: 0 session 202 | Connection Timed Out : 0 203 | Connection Rejected : 0 204 | ==================================== 205 | ``` 206 | 207 | This statistic can also be obtained while tcpeek is running by using the tcpeekstat command described below. 208 | 209 | ## tcpeekstat 210 | 211 | ``` 212 | usage: tcpeekstat [OPTION] 213 | [OPTION] 214 | -g --gmetric # exec gmetric 215 | -U --socket=path # unix domain socket (default: /var/run/tcpeek/tcpeek.sock) 216 | -v --version # version 217 | -h --help # help 218 | ``` 219 | 220 | you can run tcpeekstat to get statistics from a running tcpeek. 221 | 222 | ``` 223 | $ sudo ./tcpeekstat 224 | ``` 225 | 226 | run with the `-g` option to output the rrd via Ganglia's 'gmetric' command. 227 | 228 | ``` 229 | $ sudo ./tcpeekstat -g 230 | ``` 231 | 232 | > when `-g` option is not selected, the difference is output from the accumulated data at startup, and when it was last executed with `-g` option if there is a `-g` option 233 | 234 | ## Notes 235 | 236 | `libpcap` must be installed (libpcap recommends the latest version http://www.tcpdump.org/#latest-release). 237 | 238 | The author is not responsible for any damage caused by using this software. 239 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo -n "checking for autoreconf... " 6 | which autoreconf || { 7 | echo "*** No autoreconf found, please install it ***" 8 | exit 1 9 | } 10 | 11 | echo "running autoreconf --force --install --verbose" 12 | autoreconf --force --install --verbose || exit $? 13 | 14 | exit 0 15 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.61]) 5 | AC_INIT([tcpeek], [0.1.0], [https://github.com/pandax381/tcpeek/issues/]) 6 | AM_INIT_AUTOMAKE(foreign) 7 | AC_CONFIG_SRCDIR([src/tcpeek.c]) 8 | AC_CONFIG_HEADERS([config.h]) 9 | AC_CONFIG_MACRO_DIR([m4]) 10 | 11 | # Checks for typedefs, structures, and compiler characteristics. 12 | AC_DEFINE([_BSD_SOURCE], 1, [Enable _BSD_SOURCE]) 13 | AC_DEFINE([__USE_BSD], 1, [Enable __USE_BSD]) 14 | AC_DEFINE([__FAVOR_BSD], 1, [Enable __FAVOR_BSD]) 15 | 16 | # Checks for programs. 17 | AC_PROG_CC 18 | 19 | # Checks for header files. 20 | AC_CHECK_HEADERS([arpa/inet.h limits.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h syslog.h unistd.h]) 21 | 22 | # Checks for typedefs, structures, and compiler characteristics. 23 | AC_TYPE_UID_T 24 | AC_TYPE_SIZE_T 25 | AC_TYPE_SSIZE_T 26 | AC_TYPE_UINT16_T 27 | AC_TYPE_UINT32_T 28 | AC_TYPE_UINT8_T 29 | 30 | # Checks for library functions. 31 | AC_FUNC_MALLOC 32 | AC_CHECK_FUNCS([gettimeofday localtime_r memmove memset socket strchr strdup strerror strndup strstr strtol]) 33 | AX_PTHREAD 34 | 35 | # Checks for libpcap 36 | AC_ARG_WITH([libpcap],[AC_HELP_STRING([--with-libpcap=DIR],[location of the libpcap installed directory])],[PCAP_PATH="$withval"],[]) 37 | if test "$PCAP_PATH"; then 38 | PCAP_LDFLAGS="-L${PCAP_PATH}/lib -Wl,-rpath,${PCAP_PATH}/lib" 39 | PCAP_CPPFLAGS="-I${PCAP_PATH}/include" 40 | fi 41 | AX_CHECK_LIBRARY([PCAP],[pcap/pcap.h],[pcap],[],[AC_MSG_ERROR([libpcap not found])]) 42 | AC_ARG_ENABLE([libpcap-static],[AC_HELP_STRING([--enable-libpcap-static],[libpcap static link])],[PCAP_STATIC="yes"],[]) 43 | if test "$PCAP_STATIC" = "yes"; then 44 | PCAP_LIBS="${PCAP_PATH}/lib/libpcap.a" 45 | else 46 | PCAP_LIBS="-lpcap" 47 | fi 48 | AC_SUBST(PCAP_LDFLAGS) 49 | AC_SUBST(PCAP_CPPFLAGS) 50 | AC_SUBST(PCAP_LIBS) 51 | 52 | AC_CONFIG_FILES([Makefile src/Makefile]) 53 | AC_OUTPUT 54 | -------------------------------------------------------------------------------- /m4/ax_check_library.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_check_library.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_LIBRARY(VARIABLE-PREFIX, HEADER-FILE, LIBRARY-FILE, 8 | # [ACTION-IF-FOUND], [ACTION-IF-NOT_FOUND]) 9 | # 10 | # DESCRIPTION 11 | # 12 | # Provides a generic test for a given library, similar in concept to the 13 | # PKG_CHECK_MODULES macro used by pkg-config. 14 | # 15 | # Most simplest libraries can be checked against simply through the 16 | # presence of a header file and a library to link to. This macro allows to 17 | # wrap around the test so that it doesn't have to be recreated each time. 18 | # 19 | # Rather than define --with-$LIBRARY arguments, it uses variables in the 20 | # same way that PKG_CHECK_MODULES does. It doesn't, though, use the same 21 | # names, since you shouldn't provide a value for LIBS or CFLAGS but rather 22 | # for LDFLAGS and CPPFLAGS, to tell the linker and compiler where to find 23 | # libraries and headers respectively. 24 | # 25 | # If the library is find, HAVE_PREFIX is defined, and in all cases 26 | # PREFIX_LDFLAGS and PREFIX_CPPFLAGS are substituted. 27 | # 28 | # Example: 29 | # 30 | # AX_CHECK_LIBRARY([LIBEVENT], [event.h], [event], [], 31 | # [AC_MSG_ERROR([Unable to find libevent])]) 32 | # 33 | # LICENSE 34 | # 35 | # Copyright (c) 2010 Diego Elio Petteno` 36 | # 37 | # This program is free software: you can redistribute it and/or modify it 38 | # under the terms of the GNU General Public License as published by the 39 | # Free Software Foundation, either version 3 of the License, or (at your 40 | # option) any later version. 41 | # 42 | # This program is distributed in the hope that it will be useful, but 43 | # WITHOUT ANY WARRANTY; without even the implied warranty of 44 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 45 | # Public License for more details. 46 | # 47 | # You should have received a copy of the GNU General Public License along 48 | # with this program. If not, see . 49 | # 50 | # As a special exception, the respective Autoconf Macro's copyright owner 51 | # gives unlimited permission to copy, distribute and modify the configure 52 | # scripts that are the output of Autoconf when processing the Macro. You 53 | # need not follow the terms of the GNU General Public License when using 54 | # or distributing such scripts, even though portions of the text of the 55 | # Macro appear in them. The GNU General Public License (GPL) does govern 56 | # all other use of the material that constitutes the Autoconf Macro. 57 | # 58 | # This special exception to the GPL applies to versions of the Autoconf 59 | # Macro released by the Autoconf Archive. When you make and distribute a 60 | # modified version of the Autoconf Macro, you may extend this special 61 | # exception to the GPL to apply to your modified version as well. 62 | 63 | #serial 4 64 | 65 | AC_DEFUN([AX_CHECK_LIBRARY], [ 66 | AC_ARG_VAR($1[_CPPFLAGS], [C preprocessor flags for ]$1[ headers]) 67 | AC_ARG_VAR($1[_LDFLAGS], [linker flags for ]$1[ libraries]) 68 | 69 | AC_CACHE_VAL(AS_TR_SH([ax_cv_have_]$1), 70 | [save_CPPFLAGS="$CPPFLAGS" 71 | save_LDFLAGS="$LDFLAGS" 72 | save_LIBS="$LIBS" 73 | 74 | AS_IF([test "x$]$1[_CPPFLAGS" != "x"], 75 | [CPPFLAGS="$CPPFLAGS $]$1[_CPPFLAGS"]) 76 | 77 | AS_IF([test "x$]$1[_LDFLAGS" != "x"], 78 | [LDFLAGS="$LDFLAGS $]$1[_LDFLAGS"]) 79 | 80 | AC_CHECK_HEADER($2, [ 81 | AC_CHECK_LIB($3, [main], 82 | [AS_TR_SH([ax_cv_have_]$1)=yes], 83 | [AS_TR_SH([ax_cv_have_]$1)=no]) 84 | ], [AS_TR_SH([ax_cv_have_]$1)=no]) 85 | 86 | CPPFLAGS="$save_CPPFLAGS" 87 | LDFLAGS="$save_LDFLAGS" 88 | LIBS="$save_LIBS" 89 | ]) 90 | 91 | AS_IF([test "$]AS_TR_SH([ax_cv_have_]$1)[" = "yes"], 92 | AC_DEFINE([HAVE_]$1, [1], [Define to 1 if ]$1[ is found]) 93 | [$4], 94 | [$5]) 95 | ]) 96 | -------------------------------------------------------------------------------- /m4/ax_pthread.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_pthread.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # This macro figures out how to build C programs using POSIX threads. It 12 | # sets the PTHREAD_LIBS output variable to the threads library and linker 13 | # flags, and the PTHREAD_CFLAGS output variable to any special C compiler 14 | # flags that are needed. (The user can also force certain compiler 15 | # flags/libs to be tested by setting these environment variables.) 16 | # 17 | # Also sets PTHREAD_CC to any special C compiler that is needed for 18 | # multi-threaded programs (defaults to the value of CC otherwise). (This 19 | # is necessary on AIX to use the special cc_r compiler alias.) 20 | # 21 | # NOTE: You are assumed to not only compile your program with these flags, 22 | # but also link it with them as well. e.g. you should link with 23 | # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS 24 | # 25 | # If you are only building threads programs, you may wish to use these 26 | # variables in your default LIBS, CFLAGS, and CC: 27 | # 28 | # LIBS="$PTHREAD_LIBS $LIBS" 29 | # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 30 | # CC="$PTHREAD_CC" 31 | # 32 | # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant 33 | # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name 34 | # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). 35 | # 36 | # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the 37 | # PTHREAD_PRIO_INHERIT symbol is defined when compiling with 38 | # PTHREAD_CFLAGS. 39 | # 40 | # ACTION-IF-FOUND is a list of shell commands to run if a threads library 41 | # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it 42 | # is not found. If ACTION-IF-FOUND is not specified, the default action 43 | # will define HAVE_PTHREAD. 44 | # 45 | # Please let the authors know if this macro fails on any platform, or if 46 | # you have any other suggestions or comments. This macro was based on work 47 | # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help 48 | # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by 49 | # Alejandro Forero Cuervo to the autoconf macro repository. We are also 50 | # grateful for the helpful feedback of numerous users. 51 | # 52 | # Updated for Autoconf 2.68 by Daniel Richard G. 53 | # 54 | # LICENSE 55 | # 56 | # Copyright (c) 2008 Steven G. Johnson 57 | # Copyright (c) 2011 Daniel Richard G. 58 | # 59 | # This program is free software: you can redistribute it and/or modify it 60 | # under the terms of the GNU General Public License as published by the 61 | # Free Software Foundation, either version 3 of the License, or (at your 62 | # option) any later version. 63 | # 64 | # This program is distributed in the hope that it will be useful, but 65 | # WITHOUT ANY WARRANTY; without even the implied warranty of 66 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 67 | # Public License for more details. 68 | # 69 | # You should have received a copy of the GNU General Public License along 70 | # with this program. If not, see . 71 | # 72 | # As a special exception, the respective Autoconf Macro's copyright owner 73 | # gives unlimited permission to copy, distribute and modify the configure 74 | # scripts that are the output of Autoconf when processing the Macro. You 75 | # need not follow the terms of the GNU General Public License when using 76 | # or distributing such scripts, even though portions of the text of the 77 | # Macro appear in them. The GNU General Public License (GPL) does govern 78 | # all other use of the material that constitutes the Autoconf Macro. 79 | # 80 | # This special exception to the GPL applies to versions of the Autoconf 81 | # Macro released by the Autoconf Archive. When you make and distribute a 82 | # modified version of the Autoconf Macro, you may extend this special 83 | # exception to the GPL to apply to your modified version as well. 84 | 85 | #serial 21 86 | 87 | AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) 88 | AC_DEFUN([AX_PTHREAD], [ 89 | AC_REQUIRE([AC_CANONICAL_HOST]) 90 | AC_LANG_PUSH([C]) 91 | ax_pthread_ok=no 92 | 93 | # We used to check for pthread.h first, but this fails if pthread.h 94 | # requires special compiler flags (e.g. on True64 or Sequent). 95 | # It gets checked for in the link test anyway. 96 | 97 | # First of all, check if the user has set any of the PTHREAD_LIBS, 98 | # etcetera environment variables, and if threads linking works using 99 | # them: 100 | if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then 101 | save_CFLAGS="$CFLAGS" 102 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 103 | save_LIBS="$LIBS" 104 | LIBS="$PTHREAD_LIBS $LIBS" 105 | AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) 106 | AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) 107 | AC_MSG_RESULT([$ax_pthread_ok]) 108 | if test x"$ax_pthread_ok" = xno; then 109 | PTHREAD_LIBS="" 110 | PTHREAD_CFLAGS="" 111 | fi 112 | LIBS="$save_LIBS" 113 | CFLAGS="$save_CFLAGS" 114 | fi 115 | 116 | # We must check for the threads library under a number of different 117 | # names; the ordering is very important because some systems 118 | # (e.g. DEC) have both -lpthread and -lpthreads, where one of the 119 | # libraries is broken (non-POSIX). 120 | 121 | # Create a list of thread flags to try. Items starting with a "-" are 122 | # C compiler flags, and other items are library names, except for "none" 123 | # which indicates that we try without any flags at all, and "pthread-config" 124 | # which is a program returning the flags for the Pth emulation library. 125 | 126 | ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" 127 | 128 | # The ordering *is* (sometimes) important. Some notes on the 129 | # individual items follow: 130 | 131 | # pthreads: AIX (must check this before -lpthread) 132 | # none: in case threads are in libc; should be tried before -Kthread and 133 | # other compiler flags to prevent continual compiler warnings 134 | # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) 135 | # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) 136 | # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) 137 | # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) 138 | # -pthreads: Solaris/gcc 139 | # -mthreads: Mingw32/gcc, Lynx/gcc 140 | # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it 141 | # doesn't hurt to check since this sometimes defines pthreads too; 142 | # also defines -D_REENTRANT) 143 | # ... -mt is also the pthreads flag for HP/aCC 144 | # pthread: Linux, etcetera 145 | # --thread-safe: KAI C++ 146 | # pthread-config: use pthread-config program (for GNU Pth library) 147 | 148 | case ${host_os} in 149 | solaris*) 150 | 151 | # On Solaris (at least, for some versions), libc contains stubbed 152 | # (non-functional) versions of the pthreads routines, so link-based 153 | # tests will erroneously succeed. (We need to link with -pthreads/-mt/ 154 | # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather 155 | # a function called by this macro, so we could check for that, but 156 | # who knows whether they'll stub that too in a future libc.) So, 157 | # we'll just look for -pthreads and -lpthread first: 158 | 159 | ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" 160 | ;; 161 | 162 | darwin*) 163 | ax_pthread_flags="-pthread $ax_pthread_flags" 164 | ;; 165 | esac 166 | 167 | # Clang doesn't consider unrecognized options an error unless we specify 168 | # -Werror. We throw in some extra Clang-specific options to ensure that 169 | # this doesn't happen for GCC, which also accepts -Werror. 170 | 171 | AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) 172 | save_CFLAGS="$CFLAGS" 173 | ax_pthread_extra_flags="-Werror" 174 | CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" 175 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], 176 | [AC_MSG_RESULT([yes])], 177 | [ax_pthread_extra_flags= 178 | AC_MSG_RESULT([no])]) 179 | CFLAGS="$save_CFLAGS" 180 | 181 | if test x"$ax_pthread_ok" = xno; then 182 | for flag in $ax_pthread_flags; do 183 | 184 | case $flag in 185 | none) 186 | AC_MSG_CHECKING([whether pthreads work without any flags]) 187 | ;; 188 | 189 | -*) 190 | AC_MSG_CHECKING([whether pthreads work with $flag]) 191 | PTHREAD_CFLAGS="$flag" 192 | ;; 193 | 194 | pthread-config) 195 | AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) 196 | if test x"$ax_pthread_config" = xno; then continue; fi 197 | PTHREAD_CFLAGS="`pthread-config --cflags`" 198 | PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" 199 | ;; 200 | 201 | *) 202 | AC_MSG_CHECKING([for the pthreads library -l$flag]) 203 | PTHREAD_LIBS="-l$flag" 204 | ;; 205 | esac 206 | 207 | save_LIBS="$LIBS" 208 | save_CFLAGS="$CFLAGS" 209 | LIBS="$PTHREAD_LIBS $LIBS" 210 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" 211 | 212 | # Check for various functions. We must include pthread.h, 213 | # since some functions may be macros. (On the Sequent, we 214 | # need a special flag -Kthread to make this header compile.) 215 | # We check for pthread_join because it is in -lpthread on IRIX 216 | # while pthread_create is in libc. We check for pthread_attr_init 217 | # due to DEC craziness with -lpthreads. We check for 218 | # pthread_cleanup_push because it is one of the few pthread 219 | # functions on Solaris that doesn't have a non-functional libc stub. 220 | # We try pthread_create on general principles. 221 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include 222 | static void routine(void *a) { a = 0; } 223 | static void *start_routine(void *a) { return a; }], 224 | [pthread_t th; pthread_attr_t attr; 225 | pthread_create(&th, 0, start_routine, 0); 226 | pthread_join(th, 0); 227 | pthread_attr_init(&attr); 228 | pthread_cleanup_push(routine, 0); 229 | pthread_cleanup_pop(0) /* ; */])], 230 | [ax_pthread_ok=yes], 231 | []) 232 | 233 | LIBS="$save_LIBS" 234 | CFLAGS="$save_CFLAGS" 235 | 236 | AC_MSG_RESULT([$ax_pthread_ok]) 237 | if test "x$ax_pthread_ok" = xyes; then 238 | break; 239 | fi 240 | 241 | PTHREAD_LIBS="" 242 | PTHREAD_CFLAGS="" 243 | done 244 | fi 245 | 246 | # Various other checks: 247 | if test "x$ax_pthread_ok" = xyes; then 248 | save_LIBS="$LIBS" 249 | LIBS="$PTHREAD_LIBS $LIBS" 250 | save_CFLAGS="$CFLAGS" 251 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 252 | 253 | # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. 254 | AC_MSG_CHECKING([for joinable pthread attribute]) 255 | attr_name=unknown 256 | for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do 257 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], 258 | [int attr = $attr; return attr /* ; */])], 259 | [attr_name=$attr; break], 260 | []) 261 | done 262 | AC_MSG_RESULT([$attr_name]) 263 | if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then 264 | AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], 265 | [Define to necessary symbol if this constant 266 | uses a non-standard name on your system.]) 267 | fi 268 | 269 | AC_MSG_CHECKING([if more special flags are required for pthreads]) 270 | flag=no 271 | case ${host_os} in 272 | aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; 273 | osf* | hpux*) flag="-D_REENTRANT";; 274 | solaris*) 275 | if test "$GCC" = "yes"; then 276 | flag="-D_REENTRANT" 277 | else 278 | # TODO: What about Clang on Solaris? 279 | flag="-mt -D_REENTRANT" 280 | fi 281 | ;; 282 | esac 283 | AC_MSG_RESULT([$flag]) 284 | if test "x$flag" != xno; then 285 | PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" 286 | fi 287 | 288 | AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], 289 | [ax_cv_PTHREAD_PRIO_INHERIT], [ 290 | AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], 291 | [[int i = PTHREAD_PRIO_INHERIT;]])], 292 | [ax_cv_PTHREAD_PRIO_INHERIT=yes], 293 | [ax_cv_PTHREAD_PRIO_INHERIT=no]) 294 | ]) 295 | AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], 296 | [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) 297 | 298 | LIBS="$save_LIBS" 299 | CFLAGS="$save_CFLAGS" 300 | 301 | # More AIX lossage: compile with *_r variant 302 | if test "x$GCC" != xyes; then 303 | case $host_os in 304 | aix*) 305 | AS_CASE(["x/$CC"], 306 | [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], 307 | [#handle absolute path differently from PATH based program lookup 308 | AS_CASE(["x$CC"], 309 | [x/*], 310 | [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], 311 | [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) 312 | ;; 313 | esac 314 | fi 315 | fi 316 | 317 | test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" 318 | 319 | AC_SUBST([PTHREAD_LIBS]) 320 | AC_SUBST([PTHREAD_CFLAGS]) 321 | AC_SUBST([PTHREAD_CC]) 322 | 323 | # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: 324 | if test x"$ax_pthread_ok" = xyes; then 325 | ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) 326 | : 327 | else 328 | ax_pthread_ok=no 329 | $2 330 | fi 331 | AC_LANG_POP 332 | ])dnl AX_PTHREAD 333 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | CFLAGS += -W -Wall -Wno-unused-parameter 2 | 3 | sbin_PROGRAMS = tcpeek 4 | dist_sbin_SCRIPTS = tcpeekstat 5 | 6 | tcpeek_SOURCES = tcpeek.c tcpeek.h init.c disassemble.c session.c cksum.c lnklist.c lnklist.h hashtable.c hashtable.h common.c checker.c listener.c filter.c 7 | tcpeek_CFLAGS = $(PCAP_CPPFLAGS) $(PTHREAD_CFLAGS) 8 | tcpeek_LDADD = $(PCAP_LIBS) $(PTHREAD_LIBS) 9 | tcpeek_LDFLAGS = $(PCAP_LDFLAGS) 10 | 11 | MAINTAINERCLEANFILES = Makefile.in 12 | -------------------------------------------------------------------------------- /src/checker.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | void * 4 | tcpeek_checker_thread(void *arg) { 5 | struct timespec ts = {TCPEEK_CHECKER_INTERVAL_SEC, 0}; 6 | struct lnklist *keys; 7 | struct hashtable_key *key; 8 | struct tcpeek_session *session; 9 | 10 | while(!g.terminate) { 11 | nanosleep(&ts, NULL); 12 | pthread_mutex_lock(&g.session.mutex); 13 | keys = hashtable_get_keys(g.session.table); 14 | lnklist_iter_init(keys); 15 | while(lnklist_iter_hasnext(keys)) { 16 | key = lnklist_iter_next(keys); 17 | session = hashtable_get(g.session.table, hashtable_key_get_key(key), hashtable_key_get_len(key)); 18 | if(tcpeek_session_istimeout(session)) { 19 | tcpeek_session_timeout(session); 20 | } 21 | } 22 | lnklist_destroy(keys); 23 | pthread_mutex_unlock(&g.session.mutex); 24 | } 25 | return NULL; 26 | } 27 | -------------------------------------------------------------------------------- /src/cksum.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | uint16_t 4 | cksum16(uint16_t *data, uint16_t size, uint32_t init) { 5 | uint32_t sum; 6 | 7 | sum = init; 8 | while(size > 1) { 9 | sum += *(data++); 10 | size -= 2; 11 | } 12 | if(size) { 13 | sum += *(uint8_t *)data; 14 | } 15 | sum = (sum & 0xffff) + (sum >> 16); 16 | sum = (sum & 0xffff) + (sum >> 16); 17 | return ~(uint16_t)sum; 18 | } 19 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | int 4 | strisempty(const char *str) { 5 | return (!str || str[0] == '\0') ? 1 : 0; 6 | } 7 | 8 | int 9 | strisequal(const char *str1, const char *str2) { 10 | return (str1 && str2) ? ((strcmp(str1, str2) == 0) ? 1 : 0) : 0; 11 | } 12 | 13 | char * 14 | strtrim(char *str) { 15 | char *sp, *ep; 16 | 17 | if(!str) { 18 | return NULL; 19 | } 20 | for(sp = str; *sp; sp++) { 21 | if(!isspace(*sp)) { 22 | break; 23 | } 24 | } 25 | for(ep = (str + strlen(str)); ep > sp; ep--) { 26 | if(!isspace(*(ep - 1))) { 27 | break; 28 | } 29 | } 30 | memmove(str, sp, ep - sp); 31 | str[ep - sp] = '\0'; 32 | return str; 33 | } 34 | 35 | int 36 | strisdigit(const char *str) { 37 | if(strisempty(str)) { 38 | return 0; 39 | } 40 | while(*str) { 41 | if(!isdigit(*(str++))) { 42 | return 0; 43 | } 44 | } 45 | return 1; 46 | } 47 | 48 | struct lnklist * 49 | strsplit(const char *str, const char *sep, size_t num) { 50 | struct lnklist *dst; 51 | int seplen, count; 52 | char *sp, *ep, *piece; 53 | 54 | if(strisempty(str) || strisempty(sep)) { 55 | return NULL; 56 | } 57 | dst = lnklist_create(); 58 | if(!dst) { 59 | return NULL; 60 | } 61 | sp = (char *)str; 62 | seplen = strlen(sep); 63 | for(count = 0; count < (int)num || num == 0; count++) { 64 | ep = strstr(sp, sep); 65 | piece = ep ? strndup(sp, ep - sp) : strdup(sp); 66 | if(!piece || !lnklist_add_tail(dst, piece)) { 67 | free(piece); 68 | lnklist_destroy_with_destructor(dst, free); 69 | return NULL; 70 | } 71 | if(!ep) { 72 | break; 73 | } 74 | sp = ep + seplen; 75 | } 76 | return dst; 77 | } 78 | 79 | void * 80 | memdup(const void *s, size_t n) { 81 | void *dst; 82 | 83 | return (dst = malloc(n)) ? memcpy(dst, s, n) : NULL; 84 | } 85 | 86 | #if !defined(__USE_XOPEN2K8) && !defined(__USE_GNU) 87 | char * 88 | strndup(const char *s1, size_t n) { 89 | char *dst; 90 | dst = malloc(n + 1); 91 | if(dst) { 92 | strncpy(dst, s1, n); 93 | dst[n] = 0x00; 94 | } 95 | return dst; 96 | } 97 | #endif 98 | 99 | struct timeval * 100 | tvsub(struct timeval *a, struct timeval *b, struct timeval *res) { 101 | res->tv_sec = a->tv_sec - b->tv_sec; 102 | res->tv_usec = a->tv_usec - b->tv_usec; 103 | if(res->tv_usec < 0) { 104 | res->tv_sec -= 1; 105 | res->tv_usec += 1000000; 106 | } 107 | return res; 108 | } 109 | 110 | struct timeval * 111 | tvadd(struct timeval *a, struct timeval *b) { 112 | a->tv_sec += b->tv_sec; 113 | if(a->tv_usec + b->tv_usec >= 1000000) { 114 | a->tv_sec++; 115 | a->tv_usec = a->tv_usec + b->tv_usec - 1000000; 116 | } 117 | else { 118 | a->tv_usec = a->tv_usec + b->tv_usec; 119 | } 120 | return a; 121 | } 122 | 123 | ssize_t 124 | recvsz(int socket, void *buffer, size_t length, int flags, int timeout) { 125 | struct pollfd pfd[1]; 126 | size_t done = 0; 127 | int ready, ret; 128 | 129 | pfd[0].fd = socket; 130 | pfd[0].events = POLLIN; 131 | while(done < length) { 132 | ready = poll(pfd, sizeof(pfd) / sizeof(struct pollfd), timeout); 133 | if(ready == -1) { 134 | if(errno != EINTR) { 135 | return -1; 136 | } 137 | } 138 | else if(ready == 0) { 139 | errno = ETIMEDOUT; 140 | return -1; 141 | } 142 | else { 143 | ret = recv(socket, (caddr_t)buffer + done, length - done, flags); 144 | if(ret == -1) { 145 | if(errno != EINTR) { 146 | return -1; 147 | } 148 | } 149 | else if(ret == 0) { 150 | break; 151 | } 152 | else { 153 | done += ret; 154 | } 155 | } 156 | } 157 | return done; 158 | } 159 | 160 | ssize_t 161 | recvln(int socket, char *buffer, size_t length, int flags, int *fin, int timeout) { 162 | size_t done = 0; 163 | ssize_t ret; 164 | 165 | while(done < length - 1) { 166 | ret = recvsz(socket, buffer + done, sizeof(char), flags, timeout); 167 | if(ret == -1) { 168 | if(errno != EINTR) { 169 | return -1; 170 | } 171 | } 172 | else if(ret == 0) { 173 | if(fin) { 174 | *fin = 1; 175 | } 176 | break; 177 | } 178 | else { 179 | if(++done >= 2 && buffer[done - 1] == 0x0a && buffer[done - 2] == 0x0d) { 180 | break; 181 | } 182 | } 183 | } 184 | buffer[done] = 0x00; 185 | return done; 186 | } 187 | -------------------------------------------------------------------------------- /src/disassemble.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | static uint8_t * 4 | tcpeek_disassemble_ether(const uint8_t *packet, uint16_t plen, struct ether_header *dst) { 5 | struct ether_header *ether; 6 | 7 | ether = (struct ether_header *)packet; 8 | if(!ether || plen < sizeof(struct ether_header)) { 9 | return NULL; 10 | } 11 | if(ntohs(ether->ether_type) != ETHERTYPE_IP) { 12 | return NULL; 13 | } 14 | memcpy(dst, ether, sizeof(struct ether_header)); 15 | return (uint8_t *)(ether + 1); 16 | } 17 | 18 | static uint8_t * 19 | tcpeek_disassemble_sll(const uint8_t *packet, uint16_t plen, struct sll_header *dst) { 20 | struct sll_header *sll; 21 | 22 | sll = (struct sll_header *)packet; 23 | if(!sll || plen < sizeof(struct sll_header)) { 24 | return NULL; 25 | } 26 | if(ntohs(sll->sll_protocol) != ETHERTYPE_IP) { 27 | return NULL; 28 | } 29 | memcpy(dst, sll, sizeof(struct sll_header)); 30 | return (uint8_t *)(sll + 1); 31 | } 32 | 33 | static uint8_t * 34 | tcpeek_disassemble_datalink(const uint8_t *packet, int plen, int datalink, struct tcpeek_segment_datalink *dst) { 35 | switch(datalink) { 36 | case DLT_EN10MB: 37 | return tcpeek_disassemble_ether(packet, plen, &dst->hdr.ether); 38 | case DLT_LINUX_SLL: 39 | return tcpeek_disassemble_sll(packet, plen, &dst->hdr.sll); 40 | } 41 | return NULL; // does not reached. 42 | } 43 | 44 | static uint8_t * 45 | tcpeek_disassemble_ip(const uint8_t *packet, uint16_t plen, int datalink, struct tcpeek_segment_ip *dst) { 46 | struct ip *ip; 47 | uint16_t hlen, sum; 48 | 49 | ip = (struct ip *)tcpeek_disassemble_datalink(packet, plen, datalink, &dst->datalink); 50 | if(!ip) { 51 | return NULL; 52 | } 53 | plen -= (caddr_t)ip - (caddr_t)packet; 54 | if(plen < sizeof(struct ip)) { 55 | return NULL; 56 | } 57 | hlen = ip->ip_hl << 2; 58 | if(plen < hlen || plen < ntohs(ip->ip_len)) { 59 | return NULL; 60 | } 61 | if(g.option.checksum & TCPEEK_CKSUM_IP) { 62 | sum = cksum16((uint16_t *)ip, hlen, 0); 63 | if(sum != 0) { 64 | lprintf(LOG_WARNING, "%s [warning] IP checksum error. %04X (%04X)", __func__, sum, ip->ip_sum); 65 | return NULL; 66 | } 67 | } 68 | if(ip->ip_p != IPPROTO_TCP && (g.option.icmp ? ip->ip_p != IPPROTO_ICMP : 1)) { 69 | return NULL; 70 | } 71 | memcpy(&dst->hdr, ip, sizeof(struct ip)); 72 | return (uint8_t *)((caddr_t)ip + hlen); 73 | } 74 | 75 | static uint8_t * 76 | tcpeek_disassemble_tcp(const uint8_t *packet, uint16_t plen, int datalink, struct tcpeek_segment_tcp *dst) { 77 | uint8_t *payload; 78 | struct tcphdr *tcphdr; 79 | uint16_t hlen, tcplen, sum; 80 | struct ip *ip; 81 | uint32_t pseudo = 0; 82 | 83 | payload = tcpeek_disassemble_ip(packet, plen, datalink, &dst->ip); 84 | if(!payload) { 85 | return NULL; 86 | } 87 | if(dst->ip.hdr.ip_p == IPPROTO_ICMP) { 88 | return payload; 89 | } 90 | tcphdr = (struct tcphdr *)payload; 91 | if(!tcphdr) { 92 | return NULL; 93 | } 94 | plen -= (caddr_t)tcphdr - (caddr_t)packet; 95 | if(plen < sizeof(struct tcphdr)) { 96 | return NULL; 97 | } 98 | hlen = tcphdr->th_off << 2; 99 | if(plen < hlen) { 100 | return NULL; 101 | } 102 | ip = &dst->ip.hdr; 103 | tcplen = ntohs(ip->ip_len) - (ip->ip_hl << 2); 104 | if(g.option.checksum & TCPEEK_CKSUM_TCP) { 105 | pseudo += ip->ip_src.s_addr >> 16; 106 | pseudo += ip->ip_src.s_addr & 0xffff; 107 | pseudo += ip->ip_dst.s_addr >> 16; 108 | pseudo += ip->ip_dst.s_addr & 0xffff; 109 | pseudo += htons(IPPROTO_TCP); 110 | pseudo += htons(tcplen); 111 | sum = cksum16((uint16_t *)tcphdr, tcplen, pseudo); 112 | if(sum != 0) { 113 | lprintf(LOG_WARNING, "%s [warning] TCP checksum error. %04X (%04X)", __func__, sum, tcphdr->th_sum); 114 | return NULL; 115 | } 116 | } 117 | dst->psize = tcplen - hlen; 118 | memcpy(&dst->hdr, tcphdr, sizeof(struct tcphdr)); 119 | return (uint8_t *)((caddr_t)tcphdr + hlen); 120 | } 121 | 122 | uint8_t * 123 | tcpeek_disassemble(const uint8_t *data, uint16_t size, int datalink, struct tcpeek_segment *dst) { 124 | uint8_t *payload; 125 | struct icmp *icmp; 126 | struct ip *ip; 127 | uint16_t icmplen, sum; 128 | 129 | payload = tcpeek_disassemble_tcp(data, size, datalink, &dst->tcp); 130 | if(!payload) { 131 | return NULL; 132 | } 133 | if(dst->tcp.ip.hdr.ip_p == IPPROTO_ICMP) { 134 | icmp = (struct icmp *)payload; 135 | size -= (caddr_t)icmp - (caddr_t)data; 136 | if(size < sizeof(struct icmp)) { 137 | return NULL; 138 | } 139 | ip = &dst->tcp.ip.hdr; 140 | icmplen = ntohs(ip->ip_len) - (ip->ip_hl << 2); 141 | if(size < icmplen) { 142 | return NULL; 143 | } 144 | if(g.option.checksum & TCPEEK_CKSUM_IP) { 145 | sum = cksum16((uint16_t *)icmp, icmplen, 0); 146 | if(sum != 0) { 147 | lprintf(LOG_WARNING, "%s [warning] ICMP checksum error. %04X (%04X)", __func__, sum, icmp->icmp_cksum); 148 | return NULL; 149 | } 150 | } 151 | if(icmp->icmp_type != ICMP_UNREACH || (icmp->icmp_code != ICMP_UNREACH_PORT && icmp->icmp_code != ICMP_UNREACH_HOST)) { 152 | return NULL; 153 | } 154 | dst->icmp_unreach = 1; 155 | ip = (struct ip *)icmp->icmp_data; 156 | memcpy(&dst->tcp.ip.hdr, icmp->icmp_data, sizeof(struct ip)); 157 | memcpy(&dst->tcp.hdr, icmp->icmp_data + (ip->ip_hl << 2), 8); 158 | return (uint8_t *)((caddr_t)icmp + icmplen); 159 | } 160 | return payload; 161 | } 162 | -------------------------------------------------------------------------------- /src/filter.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | static int 4 | tcpeek_filter_parse_rule(struct tcpeek_filter_rule *rule, const char *expression); 5 | static uint32_t 6 | getnwaddr(uint32_t addr, uint8_t prefix); 7 | 8 | struct tcpeek_filter * 9 | tcpeek_filter_create(void) { 10 | struct tcpeek_filter *filter; 11 | 12 | filter = malloc(sizeof(struct tcpeek_filter)); 13 | if(filter) { 14 | memset(filter, 0x00, sizeof(struct tcpeek_filter)); 15 | filter->rule = lnklist_create(); 16 | if(!filter->rule) { 17 | free(filter); 18 | return NULL; 19 | } 20 | } 21 | return filter; 22 | } 23 | 24 | void 25 | tcpeek_filter_destroy(struct tcpeek_filter *filter) { 26 | if(!filter) { 27 | free(filter->stat); 28 | lnklist_destroy_with_destructor(filter->rule, (void (*)(void *))tcpeek_filter_rule_destroy); 29 | free(filter); 30 | } 31 | } 32 | 33 | struct tcpeek_filter_rule * 34 | tcpeek_filter_rule_create(void) { 35 | struct tcpeek_filter_rule *rule; 36 | 37 | rule = malloc(sizeof(struct tcpeek_filter_rule)); 38 | if(rule) { 39 | memset(rule, 0x00, sizeof(struct tcpeek_filter_rule)); 40 | rule->port = lnklist_create(); 41 | if(!rule->port) { 42 | free(rule); 43 | return NULL; 44 | } 45 | } 46 | return rule; 47 | } 48 | 49 | void 50 | tcpeek_filter_rule_destroy(struct tcpeek_filter_rule *rule) { 51 | if(rule) { 52 | lnklist_destroy_with_destructor(rule->port, free); 53 | free(rule); 54 | } 55 | } 56 | 57 | int 58 | tcpeek_filter_parse(struct tcpeek_filter *filter, const char *expression) { 59 | char *sp, *ep; 60 | struct lnklist *list; 61 | struct tcpeek_filter_rule *rule; 62 | 63 | sp = (char *)expression; 64 | if(!(ep = strchr(sp, ':'))) { 65 | return -1; 66 | } 67 | if(ep != sp) { 68 | strncpy(filter->name, sp, ep - sp); 69 | filter->stat = malloc(sizeof(struct tcpeek_stat) * 2); 70 | if(!filter->stat) { 71 | return -1; 72 | } 73 | memset(filter->stat, 0x00, sizeof(struct tcpeek_stat) * 2); 74 | } 75 | sp = ep + 1; 76 | if(!(ep = strchr(sp, '@')) || ep - sp != 2) { 77 | return -1; 78 | } 79 | if(strncmp(sp, "RX", 2) == 0) { 80 | filter->dir = TCPEEK_FILTER_DIR_RX; 81 | } 82 | else if(strncmp(sp, "TX", 2) == 0) { 83 | filter->dir = TCPEEK_FILTER_DIR_TX; 84 | } 85 | else { 86 | return -1; 87 | } 88 | sp = ep + 1; 89 | list = strsplit(sp, ",", 0); 90 | if(lnklist_size(list) < 1) { 91 | lnklist_destroy_with_destructor(list, free); 92 | return -1; 93 | } 94 | lnklist_iter_init(list); 95 | while(lnklist_iter_hasnext(list)) { 96 | rule = tcpeek_filter_rule_create(); 97 | if(tcpeek_filter_parse_rule(rule, lnklist_iter_next(list)) == -1) { 98 | tcpeek_filter_rule_destroy(rule); 99 | lnklist_destroy_with_destructor(list, free); 100 | return -1; 101 | } 102 | if(!lnklist_add_tail(filter->rule, rule)) { 103 | tcpeek_filter_rule_destroy(rule); 104 | lnklist_destroy_with_destructor(list, free); 105 | return -1; 106 | } 107 | } 108 | lnklist_destroy_with_destructor(list, free); 109 | return 0; 110 | } 111 | 112 | static int 113 | tcpeek_filter_parse_rule(struct tcpeek_filter_rule *rule, const char *expression) { 114 | struct lnklist *list; 115 | char *prefix, *addr, *port; 116 | uint16_t portno; 117 | 118 | list = strsplit(expression, ":", 0); 119 | if(lnklist_size(list) < 2) { 120 | lnklist_destroy(list); 121 | return -1; 122 | } 123 | lnklist_iter_init(list); 124 | addr = lnklist_iter_next(list); 125 | if(strisequal(addr, "*") || strisequal(addr, "%")) { 126 | rule->addr.s_addr = htonl(INADDR_ANY); 127 | } 128 | else { 129 | if((prefix = strchr(addr, '/'))) { 130 | *(prefix++) = '\0'; 131 | if(strisdigit(prefix) == 0) { 132 | lnklist_destroy_with_destructor(list, free); 133 | return -1; 134 | } 135 | rule->prefix = (uint8_t)strtol(prefix, NULL, 10); 136 | if(rule->prefix > 32) { 137 | lnklist_destroy_with_destructor(list, free); 138 | return -1; 139 | } 140 | } 141 | else { 142 | rule->prefix = 32; 143 | } 144 | if(inet_pton(AF_INET, addr, &rule->addr) != 1) { 145 | lnklist_destroy_with_destructor(list, free); 146 | return -1; 147 | } 148 | } 149 | while(lnklist_iter_hasnext(list)) { 150 | port = lnklist_iter_next(list); 151 | if(strisequal(port, "*") || strisequal(port, "%")) { 152 | portno = 0; 153 | } 154 | else { 155 | if(strisdigit(port) == 0) { 156 | lnklist_destroy_with_destructor(list, free); 157 | return -1; 158 | } 159 | portno = htons((uint16_t)strtol(port, NULL, 10)); 160 | } 161 | if(!lnklist_add_tail(rule->port, memdup(&portno, sizeof(portno)))) { 162 | lnklist_destroy_with_destructor(list, free); 163 | return -1; 164 | } 165 | } 166 | lnklist_destroy_with_destructor(list, free); 167 | return 0; 168 | } 169 | 170 | struct lnklist * 171 | tcpeek_filter_lookup(struct tcpeek_segment *segment) { 172 | struct lnklist *dst = NULL; 173 | struct tcpeek_filter *filter; 174 | struct tcpeek_filter_rule *rule; 175 | uint16_t *port; 176 | 177 | lnklist_iter_init(g.filter); 178 | while(lnklist_iter_hasnext(g.filter)) { 179 | filter = lnklist_iter_next(g.filter); 180 | lnklist_iter_init(filter->rule); 181 | while(lnklist_iter_hasnext(filter->rule)) { 182 | rule = lnklist_iter_next(filter->rule); 183 | if(filter->dir == TCPEEK_FILTER_DIR_RX) { 184 | if(segment->tcp.ip.hdr.ip_dst.s_addr != g.addr.unicast.s_addr) { 185 | continue; 186 | } 187 | if(rule->addr.s_addr != htonl(INADDR_ANY) && getnwaddr(segment->tcp.ip.hdr.ip_src.s_addr, rule->prefix) != rule->addr.s_addr) { 188 | continue; 189 | } 190 | } 191 | else { 192 | if(segment->tcp.ip.hdr.ip_src.s_addr != g.addr.unicast.s_addr) { 193 | continue; 194 | } 195 | if(rule->addr.s_addr != htonl(INADDR_ANY) && getnwaddr(segment->tcp.ip.hdr.ip_dst.s_addr, rule->prefix) != rule->addr.s_addr) { 196 | continue; 197 | } 198 | } 199 | lnklist_iter_init(rule->port); 200 | while(lnklist_iter_hasnext(rule->port)) { 201 | port = lnklist_iter_next(rule->port); 202 | if(*port == 0 || *port == segment->tcp.hdr.th_dport) { 203 | if(filter->stat) { 204 | if(!dst) { 205 | dst = lnklist_create(); 206 | } 207 | lnklist_add_tail(dst, filter->stat); 208 | } 209 | else { 210 | return NULL; 211 | } 212 | } 213 | } 214 | } 215 | } 216 | return dst; 217 | } 218 | 219 | static uint32_t 220 | getnwaddr(uint32_t addr, uint8_t prefix) { 221 | return htonl(ntohl(addr) & (uint32_t)0xffffffff << (32 - prefix)); 222 | } 223 | -------------------------------------------------------------------------------- /src/hashtable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "hashtable.h" 4 | 5 | struct hashtable_key { 6 | size_t len; 7 | void *key; 8 | }; 9 | 10 | struct hashtable_entry { 11 | struct hashtable_key key; 12 | void *value; 13 | }; 14 | 15 | struct hashtable { 16 | size_t capacity; 17 | size_t num; 18 | struct lnklist **rows; 19 | }; 20 | 21 | static struct hashtable_entry * 22 | hashtable_entry_create(void); 23 | static void * 24 | hashtable_entry_destroy(struct hashtable_entry *obj); 25 | static void * 26 | hashtable_entry_set(struct hashtable_entry *obj, const void *key, size_t klen, void *value); 27 | static void * 28 | hashtable_entry_get_value(struct hashtable_entry *obj); 29 | static int 30 | hashtable_entry_isequal_key(struct hashtable_entry *obj, const void *key, size_t klen); 31 | static unsigned int 32 | hash(const void *key, size_t klen, size_t threshold); 33 | 34 | extern void * 35 | memdup(const void *s, size_t n); 36 | 37 | #ifdef __HASHTABLE_DEBUG__ 38 | int 39 | main(int argc, char *argv[]) { 40 | struct hashtable *ht; 41 | int count; 42 | char buf[128]; 43 | struct lnklist *keys; 44 | struct lnklist_key *key; 45 | 46 | ht = hashtable_create(1000); 47 | if(!ht) { 48 | fprintf(stderr, "hashtable_create(): error.\n"); 49 | return -1; 50 | } 51 | for(count = 0; count < 100000; count++) { 52 | snprintf(buf, sizeof(buf), "%d", count); 53 | hashtable_put(ht, &count, sizeof(count), strdup(buf)); 54 | } 55 | fprintf(stderr, "### hashtable_size(): %d\n", hashtable_size(ht)); 56 | hashtable_debug_print(ht, stderr); 57 | keys = hashtable_get_keys(ht); 58 | lnklist_iter_init(keys); 59 | while(lnklist_iter_hasnext(keys)) { 60 | key = lnklist_iter_next(keys); 61 | free(hashtable_remove(ht, hashtable_key_get_key(key), hashtable_key_get_len(key))); 62 | } 63 | lnklist_destroy(keys); 64 | fprintf(stderr, "### hashtable_size(): %d\n", hashtable_size(ht)); 65 | hashtable_debug_print(ht, stderr); 66 | hashtable_destroy(ht); 67 | return 0; 68 | } 69 | #endif 70 | 71 | struct hashtable * 72 | hashtable_create(size_t capacity) { 73 | struct hashtable *obj; 74 | size_t index; 75 | 76 | obj = (struct hashtable *)malloc(sizeof(struct hashtable)); 77 | if(!obj) { 78 | return NULL; 79 | } 80 | obj->capacity = capacity; 81 | obj->num = 0; 82 | obj->rows = (struct lnklist **)malloc(obj->capacity * sizeof(struct lnklist *)); 83 | if(!obj->rows) { 84 | free(obj); 85 | return NULL; 86 | } 87 | for(index = 0; index < obj->capacity; index++) { 88 | obj->rows[index] = NULL; 89 | } 90 | return obj; 91 | } 92 | 93 | void 94 | hashtable_destroy(struct hashtable *obj) { 95 | size_t index; 96 | struct lnklist *row; 97 | 98 | if(!obj) { 99 | return; 100 | } 101 | for(index = 0; index < obj->capacity; index++) { 102 | row = obj->rows[index]; 103 | while(lnklist_size(row) > 0) { 104 | hashtable_entry_destroy(lnklist_remove(row, 0)); 105 | } 106 | lnklist_destroy(row); 107 | } 108 | free(obj->rows); 109 | free(obj); 110 | } 111 | 112 | void * 113 | hashtable_put(struct hashtable *obj, const void *key, size_t klen, void *value) { 114 | unsigned int index; 115 | struct lnklist *row; 116 | struct hashtable_entry *entry; 117 | 118 | if(!obj || !key || !klen) { 119 | return NULL; 120 | } 121 | index = hash(key, klen, obj->capacity); 122 | row = obj->rows[index]; 123 | if(!row) { 124 | row = obj->rows[index] = lnklist_create(); 125 | if(!row) { 126 | return NULL; 127 | } 128 | } 129 | else { 130 | lnklist_iter_init(row); 131 | while(lnklist_iter_hasnext(row)) { 132 | entry = (struct hashtable_entry *)lnklist_iter_next(row); 133 | if(hashtable_entry_isequal_key(entry, key, klen)) { 134 | return NULL; 135 | } 136 | } 137 | } 138 | entry = hashtable_entry_create(); 139 | if(!entry) { 140 | return NULL; 141 | } 142 | hashtable_entry_set(entry, key, klen, value); 143 | lnklist_add_tail(row, entry); 144 | obj->num++; 145 | return value; 146 | } 147 | 148 | 149 | void * 150 | hashtable_remove(struct hashtable *obj, const void *key, size_t klen) { 151 | unsigned int index; 152 | struct lnklist *row; 153 | void *value = NULL; 154 | struct hashtable_entry *entry; 155 | 156 | if(!obj || !key || !klen) { 157 | return NULL; 158 | } 159 | index = hash(key, klen, obj->capacity); 160 | row = obj->rows[index]; 161 | lnklist_iter_init(row); 162 | while(lnklist_iter_hasnext(row)) { 163 | entry = (struct hashtable_entry *)lnklist_iter_next(row); 164 | if(hashtable_entry_isequal_key(entry, key, klen)) { 165 | value = hashtable_entry_destroy(lnklist_iter_remove(row)); 166 | obj->num--; 167 | break; 168 | } 169 | } 170 | return value; 171 | } 172 | 173 | void * 174 | hashtable_get(struct hashtable *obj, const void *key, size_t klen) { 175 | unsigned int index; 176 | struct lnklist *row; 177 | void *value = NULL; 178 | struct hashtable_entry *entry; 179 | 180 | if(!obj || !key || !klen) { 181 | return NULL; 182 | } 183 | index = hash(key, klen, obj->capacity); 184 | row = obj->rows[index]; 185 | lnklist_iter_init(row); 186 | while(lnklist_iter_hasnext(row)) { 187 | entry = (struct hashtable_entry *)lnklist_iter_next(row); 188 | if(hashtable_entry_isequal_key(entry, key, klen)) { 189 | value = hashtable_entry_get_value(entry); 190 | break; 191 | } 192 | } 193 | return value; 194 | } 195 | 196 | struct lnklist * 197 | hashtable_get_keys(struct hashtable *obj) { 198 | struct lnklist *keys, *row; 199 | size_t index; 200 | struct hashtable_entry *entry; 201 | 202 | if(!obj) { 203 | return NULL; 204 | } 205 | keys = lnklist_create(); 206 | for(index = 0; index < obj->capacity; index++) { 207 | row = obj->rows[index]; 208 | lnklist_iter_init(row); 209 | while(lnklist_iter_hasnext(row)) { 210 | entry = (struct hashtable_entry *)lnklist_iter_next(row); 211 | lnklist_add_tail(keys, &entry->key); 212 | } 213 | } 214 | return keys; 215 | } 216 | 217 | int 218 | hashtable_isempty(struct hashtable *obj) { 219 | return obj ? (obj->num > 0 ? 0 : 1) : -1; 220 | } 221 | 222 | ssize_t 223 | hashtable_size(struct hashtable *obj) { 224 | return obj ? (ssize_t)obj->num : -1; 225 | } 226 | 227 | void 228 | hashtable_debug_print(struct hashtable *obj, FILE *fp) { 229 | size_t index; 230 | struct lnklist *row; 231 | 232 | if(!fp || !obj) { 233 | return; 234 | } 235 | for(index = 0; index < obj->capacity; index++) { 236 | row = obj->rows[index]; 237 | fprintf(fp, "hashtable[%zd]: %zd\n", index, row ? lnklist_size(row) : 0); 238 | } 239 | } 240 | 241 | static struct hashtable_entry * 242 | hashtable_entry_create(void) { 243 | struct hashtable_entry *obj; 244 | 245 | obj = (struct hashtable_entry *)malloc(sizeof(struct hashtable_entry)); 246 | if(obj) { 247 | obj->key.len = 0; 248 | obj->key.key = NULL; 249 | obj->value = NULL; 250 | } 251 | return obj; 252 | } 253 | 254 | static void * 255 | hashtable_entry_destroy(struct hashtable_entry *obj) { 256 | void *value = NULL; 257 | 258 | if(obj) { 259 | free(obj->key.key); 260 | value = obj->value; 261 | free(obj); 262 | } 263 | return value; 264 | } 265 | 266 | static void * 267 | hashtable_entry_set(struct hashtable_entry *obj, const void *key, size_t klen, void *value) { 268 | void *old_value = NULL; 269 | 270 | if(!obj || !key || !klen) { 271 | return NULL; 272 | } 273 | obj->key.len = klen; 274 | free(obj->key.key); 275 | obj->key.key = memdup(key, klen); 276 | if(obj->value) { 277 | old_value = obj->value; 278 | } 279 | obj->value = value; 280 | return old_value; 281 | } 282 | 283 | static void * 284 | hashtable_entry_get_value(struct hashtable_entry *obj) { 285 | return obj ? obj->value : NULL; 286 | } 287 | 288 | static int 289 | hashtable_entry_isequal_key(struct hashtable_entry *obj, const void *key, size_t klen) { 290 | return (obj && obj->key.len == klen && memcmp(obj->key.key, key, klen) == 0) ? 1 : 0; 291 | } 292 | 293 | void * 294 | hashtable_key_get_key(struct hashtable_key *obj) { 295 | return obj ? obj->key : NULL; 296 | } 297 | 298 | ssize_t 299 | hashtable_key_get_len(struct hashtable_key *obj) { 300 | return obj ? (ssize_t)obj->len : -1; 301 | } 302 | 303 | static unsigned int 304 | hash(const void *key, size_t klen, size_t threshold) { 305 | size_t index; 306 | unsigned int h = 0; 307 | 308 | for(index = 0; index < klen; index++) { 309 | h = (h * 137) + ((unsigned char *)key)[index]; 310 | } 311 | return h % threshold; 312 | } 313 | -------------------------------------------------------------------------------- /src/hashtable.h: -------------------------------------------------------------------------------- 1 | #ifndef __HASHTABLE_H__ 2 | #define __HASHTABLE_H__ 3 | 4 | #include 5 | #include 6 | #include "lnklist.h" 7 | 8 | struct hashtable; 9 | struct hashtable_key; 10 | 11 | extern struct hashtable * 12 | hashtable_create(size_t capacity); 13 | extern void 14 | hashtable_destroy(struct hashtable *obj); 15 | extern void * 16 | hashtable_put(struct hashtable *obj, const void *key, size_t klen, void *value); 17 | extern void * 18 | hashtable_remove(struct hashtable *obj, const void *key, size_t klen); 19 | extern void * 20 | hashtable_get(struct hashtable *obj, const void *key, size_t klen); 21 | extern struct lnklist * 22 | hashtable_get_keys(struct hashtable *obj); 23 | extern int 24 | hashtable_isempty(struct hashtable *obj); 25 | extern ssize_t 26 | hashtable_size(struct hashtable *obj); 27 | extern void 28 | hashtable_debug_print(struct hashtable *obj, FILE *fp); 29 | extern void * 30 | hashtable_key_get_key(struct hashtable_key *obj); 31 | extern ssize_t 32 | hashtable_key_get_len(struct hashtable_key *obj); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | #define DEFAULT_PCAP_SNAPLEN 138 4 | 5 | static void 6 | tcpeek_init_global(void); 7 | static void 8 | tcpeek_init_option(int argc, char *argv[]); 9 | static void 10 | tcpeek_init_signal(void); 11 | static void 12 | tcpeek_init_log(void); 13 | static void 14 | tcpeek_init_session(void); 15 | static void 16 | tcpeek_init_filter_and_stat(void); 17 | static void 18 | tcpeek_init_pcap(void); 19 | static void 20 | tcpeek_init_addr(void); 21 | static void 22 | tcpeek_init_setuid(void); 23 | static void 24 | tcpeek_init_socket(void); 25 | static void 26 | usage(void); 27 | static void 28 | version(void); 29 | 30 | void 31 | tcpeek_init(int argc, char *argv[]) { 32 | tcpeek_init_global(); 33 | tcpeek_init_option(argc, argv); 34 | tcpeek_init_signal(); 35 | tcpeek_init_log(); 36 | tcpeek_init_session(); 37 | tcpeek_init_filter_and_stat(); 38 | tcpeek_init_pcap(); 39 | tcpeek_init_addr(); 40 | tcpeek_init_setuid(); 41 | tcpeek_init_socket(); 42 | } 43 | 44 | static void 45 | tcpeek_init_global(void) { 46 | memset(&g, 0x00, sizeof(g)); 47 | g.option.timeout = 30; 48 | g.option.buffer = 2; 49 | g.option.checksum = TCPEEK_CKSUM_IP; 50 | strncpy(g.option.socket, TCPEEK_SOCKET_FILE, sizeof(g.option.socket) - 1); 51 | g.option.expression = lnklist_create(); 52 | lnklist_add_tail(g.option.expression, strdup("RX:RX@%:%")); 53 | lnklist_add_tail(g.option.expression, strdup("TX:TX@%:%")); 54 | g.filter = lnklist_create(); 55 | g.soc = -1; 56 | } 57 | 58 | static void 59 | tcpeek_init_option(int argc, char *argv[]) { 60 | int opt; 61 | static struct option long_options[] = { 62 | {"user", 1, NULL, 'u'}, 63 | {"interface", 1, NULL, 'i'}, 64 | {"checksum", 1, NULL, 'c'}, 65 | {"socket", 1, NULL, 'U'}, 66 | {"timeout", 1, NULL, 't'}, 67 | {"buffer", 1, NULL, 'B'}, 68 | {"loglevel", 1, NULL, 'l'}, 69 | {"quiet", 0, NULL, 'q'}, 70 | {"promisc", 0, NULL, 500}, 71 | {"icmp", 0, NULL, 501}, 72 | {"help", 0, NULL, 'h'}, 73 | {"version", 0, NULL, 'v'}, 74 | { NULL, 0, NULL, 0 } 75 | }; 76 | 77 | while((opt = getopt_long_only(argc, argv, "u:i:c:U:t:B:l:qhv", long_options, NULL)) != -1) { 78 | switch(opt) { 79 | case 'u': 80 | strncpy(g.option.user, optarg, sizeof(g.option.user) - 1); 81 | break; 82 | case 'i': 83 | strncpy(g.option.ifname, optarg, sizeof(g.option.ifname) - 1); 84 | break; 85 | case 'c': 86 | if(!strisequal(optarg, "0") && !strisequal(optarg, "1") && !strisequal(optarg, "2")) { 87 | usage(); 88 | tcpeek_terminate(0); 89 | } 90 | g.option.checksum = strtol(optarg, NULL, 10); 91 | break; 92 | case 'U': 93 | strncpy(g.option.socket, optarg, sizeof(g.option.socket) - 1); 94 | break; 95 | case 'B': 96 | if(!strisdigit(optarg)) { 97 | usage(); 98 | tcpeek_terminate(0); 99 | } 100 | g.option.buffer = strtol(optarg, NULL, 10); 101 | break; 102 | case 't': 103 | if(!strisdigit(optarg)) { 104 | usage(); 105 | tcpeek_terminate(0); 106 | } 107 | g.option.timeout = strtol(optarg, NULL, 10); 108 | break; 109 | case 'q': 110 | g.option.quiet = 1; 111 | break; 112 | case 500: 113 | g.option.promisc = 1; 114 | break; 115 | case 501: 116 | g.option.icmp = 1; 117 | break; 118 | case 'h': 119 | usage(); 120 | tcpeek_terminate(0); 121 | case 'v': 122 | version(); 123 | tcpeek_terminate(0); 124 | default: 125 | usage(); 126 | tcpeek_terminate(1); 127 | } 128 | } 129 | while(optind < argc) { 130 | lnklist_add_tail(g.option.expression, strdup(argv[optind++])); 131 | } 132 | } 133 | 134 | static void 135 | tcpeek_init_signal(void) { 136 | static int signals[] = {SIGINT, SIGTERM, SIGPIPE, SIGUSR1, SIGUSR2, SIGALRM, 0}; 137 | struct sigaction sig; 138 | int offset; 139 | 140 | memset(&sig, 0, sizeof(sig)); 141 | sig.sa_handler = tcpeek_signal_handler; 142 | for(offset = 0; signals[offset]; offset++) { 143 | if(sigaction(signals[offset], &sig, NULL) == -1){ 144 | error_abort("sigaction: '%d' %s", signals[offset], strerror(errno)); 145 | } 146 | } 147 | } 148 | 149 | static void 150 | tcpeek_init_log(void) { 151 | //openlog(PACKAGE_NAME, LOG_NDELAY | LOG_PERROR, LOG_DAEMON); 152 | } 153 | 154 | static void 155 | tcpeek_init_session(void) { 156 | g.session.table = hashtable_create(TCPEEK_SESSION_TABLE_SIZE); 157 | if(!g.session.table) { 158 | error_abort("hashtable can't create."); 159 | } 160 | pthread_mutex_init(&g.session.mutex, NULL); 161 | } 162 | 163 | static void 164 | tcpeek_init_filter_and_stat(void) { 165 | char *expression; 166 | struct tcpeek_filter *filter; 167 | 168 | lnklist_iter_init(g.option.expression); 169 | while(lnklist_iter_hasnext(g.option.expression)) { 170 | expression = lnklist_iter_next(g.option.expression); 171 | filter = tcpeek_filter_create(); 172 | if(tcpeek_filter_parse(filter, expression) == -1) { 173 | tcpeek_filter_destroy(filter); 174 | error_abort("filter '%s' parse error.", expression); 175 | } 176 | if(!lnklist_add(g.filter, filter, filter->stat ? lnklist_size(g.filter) : 0)) { 177 | tcpeek_filter_destroy(filter); 178 | error_abort("can't allocate."); 179 | } 180 | } 181 | } 182 | 183 | static void 184 | tcpeek_init_pcap(void) { 185 | char *ifname, errmsg[PCAP_ERRBUF_SIZE], expression[] = "tcp or icmp"; 186 | struct bpf_program bpf; 187 | 188 | if(strisempty(g.option.ifname)) { 189 | ifname = pcap_lookupdev(errmsg); 190 | if(!ifname) { 191 | error_abort("%s", errmsg); 192 | } 193 | strncpy(g.option.ifname, ifname, sizeof(g.option.ifname) - 1); 194 | } 195 | g.pcap.pcap = pcap_create(g.option.ifname, errmsg); 196 | if(!g.pcap.pcap) { 197 | error_abort("%s", errmsg); 198 | } 199 | if(pcap_set_buffer_size(g.pcap.pcap, g.option.buffer * 1024 * 1024) != 0) { 200 | error_abort("%s", "can not set buffer size"); 201 | } 202 | if(pcap_set_snaplen(g.pcap.pcap, DEFAULT_PCAP_SNAPLEN) != 0) { 203 | error_abort("%s", "can not set snaplen"); 204 | } 205 | if(pcap_set_promisc(g.pcap.pcap, g.option.promisc) != 0) { 206 | error_abort("%s", "can not set promiscuous mode"); 207 | } 208 | if(pcap_set_timeout(g.pcap.pcap, 1) != 0) { 209 | error_abort("%s", "can not set timeout"); 210 | } 211 | if(pcap_activate(g.pcap.pcap) != 0) { 212 | error_abort("%s", pcap_geterr(g.pcap.pcap)); 213 | } 214 | if(pcap_compile(g.pcap.pcap, &bpf, expression, 0, 0) == -1) { 215 | error_abort("%s '%s'", pcap_geterr(g.pcap.pcap), expression); 216 | } 217 | if(pcap_setfilter(g.pcap.pcap, &bpf) == -1){ 218 | error_abort("%s", pcap_geterr(g.pcap.pcap)); 219 | } 220 | pcap_freecode(&bpf); 221 | g.pcap.snapshot = pcap_snapshot(g.pcap.pcap); 222 | g.pcap.datalink = pcap_datalink(g.pcap.pcap); 223 | if(g.pcap.datalink != DLT_EN10MB && g.pcap.datalink != DLT_LINUX_SLL) { 224 | error_abort("not support datalink %s (%s)", 225 | pcap_datalink_val_to_name(g.pcap.datalink), 226 | pcap_datalink_val_to_description(g.pcap.datalink) 227 | ); 228 | } 229 | } 230 | 231 | static void 232 | tcpeek_init_addr(void) { 233 | struct ifaddrs *ifap, *ifa = NULL; 234 | 235 | if(getifaddrs(&ifap) != -1) { 236 | for(ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 237 | if(strisequal(ifa->ifa_name, g.option.ifname) && ifa->ifa_addr->sa_family == AF_INET) { 238 | g.addr.unicast.s_addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; 239 | break; 240 | } 241 | } 242 | } 243 | freeifaddrs(ifap); 244 | if(!ifa) { 245 | error_abort("'%s' not found", g.option.ifname); 246 | } 247 | } 248 | 249 | static void 250 | tcpeek_init_setuid(void) { 251 | struct passwd *passwd; 252 | gid_t groups[128]; 253 | int ngroups; 254 | 255 | if(strisempty(g.option.user)) { 256 | return; 257 | } 258 | passwd = strisdigit(g.option.user) ? getpwuid((uid_t)strtol(g.option.user, NULL, 10)) : getpwnam(g.option.user); 259 | if(!passwd) { 260 | error_abort("%s", strerror(errno)); 261 | } 262 | ngroups = sizeof(groups); 263 | if(getgrouplist(g.option.user, passwd->pw_gid, groups, &ngroups) == -1) { 264 | error_abort("getgrouplist: %s", strerror(errno)); 265 | } 266 | if(setgroups(ngroups, groups) == -1) { 267 | error_abort("setgroups: %s", strerror(errno)); 268 | } 269 | if(setgid(passwd->pw_gid) == -1) { 270 | error_abort("setgid: %s", strerror(errno)); 271 | } 272 | if(setuid(passwd->pw_uid) == -1) { 273 | error_abort("setuid: %s", strerror(errno)); 274 | } 275 | } 276 | 277 | static void 278 | tcpeek_init_socket(void) { 279 | struct sockaddr_un sockaddr; 280 | 281 | g.soc = socket(PF_UNIX, SOCK_STREAM, 0); 282 | if(g.soc == -1) { 283 | error_abort("socket: %s", strerror(errno)); 284 | } 285 | memset(&sockaddr, 0, sizeof(sockaddr)); 286 | sockaddr.sun_family = PF_UNIX; 287 | strcpy(sockaddr.sun_path, g.option.socket); 288 | if(bind(g.soc, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == -1) { 289 | close(g.soc); 290 | g.soc = -1; 291 | error_abort("bind: %s", strerror(errno)); 292 | } 293 | } 294 | 295 | static void 296 | usage(void) { 297 | printf("usage: %s [option]... [expression]...\n", PACKAGE_NAME); 298 | printf(" option:\n"); 299 | printf(" -u --user=uid # set uid\n"); 300 | printf(" -i --interface=dev # network device (ex: eth0)\n"); 301 | printf(" -U --socket=path # unix domain socket (default: /var/run/tcpeek/tcpeek.sock)\n"); 302 | printf(" -c --checksum=[0|1|2] # ckecksum lookup mode 0=none 1=ip 2=tcp (default: 0)\n"); 303 | printf(" -t --timeout=sec # session timeout (default: 60)\n"); 304 | printf(" -B --buffer=MB # libpcap buffer size (default: 2)\n"); 305 | printf(" -l --loglevel=LEVEL # see man syslog (default: LOG_NOTICE)\n"); 306 | printf(" -q --quiet # quiet mode\n"); 307 | printf(" --promisc # enable promiscuous capture\n"); 308 | printf(" --icmp # enable icmp port unreachable lookup\n"); 309 | printf(" -h --help # help\n"); 310 | printf(" -v --version # version\n"); 311 | printf(" expression:\n"); 312 | printf(" filter:dir@addr:port[,port...]\n"); 313 | printf(" example) '%%' is the same as wildcard '*'\n"); 314 | printf(" tcpeek -i eth0 filter:RX@%%:80:443\n"); 315 | printf(" tcpeek -i eth0 filter:TX@192.168.0.0/24:%%\n"); 316 | printf(" tcpeek -i eth0 inbound-filter:RX@%%:%% outbound-filter:TX@192.168.0.100:%%,192.168.0.200:%%\n"); 317 | } 318 | 319 | static void 320 | version(void) { 321 | printf("%s %s (with %s)\n",PACKAGE_NAME, PACKAGE_VERSION, pcap_lib_version()); 322 | } 323 | -------------------------------------------------------------------------------- /src/listener.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | static void 4 | tcpeek_listener_stat_json(int soc, const char *method); 5 | 6 | void * 7 | tcpeek_listener_thread(void *arg) { 8 | struct pollfd pfds[1]; 9 | int ready, acc; 10 | char method[128]; 11 | 12 | if(listen(g.soc, SOMAXCONN) == -1) { 13 | lprintf(LOG_ERROR, "%s", strerror(errno)); 14 | return NULL; 15 | } 16 | pfds[0].fd = g.soc; 17 | pfds[0].events = POLLIN | POLLERR; 18 | while(!g.terminate) { 19 | if((ready = poll(pfds, sizeof(pfds) / sizeof(struct pollfd), 1000)) == -1) { 20 | if(errno != EINTR) { 21 | lprintf(LOG_ERROR, "%s", strerror(errno)); 22 | break; 23 | } 24 | } 25 | else if(ready == 0) { 26 | /* timeout */ 27 | } 28 | else { 29 | if((acc = accept(g.soc, NULL, NULL)) == -1) { 30 | continue; 31 | } 32 | if(recvln(acc, method, sizeof(method), 0, NULL, 1000) != -1) { 33 | tcpeek_listener_stat_json(acc, strtrim(method)); 34 | } 35 | close(acc); 36 | } 37 | } 38 | return NULL; 39 | } 40 | 41 | static void 42 | tcpeek_listener_stat_json(int soc, const char *method) { 43 | struct tcpeek_filter *filter; 44 | struct tcpeek_stat *stat; 45 | char success[128], failure[128], buf[512]; 46 | int isrefresh = 0, isfirst = 1; 47 | 48 | pthread_mutex_lock(&g.session.mutex); 49 | send(soc, "[", 1, 0); 50 | lnklist_iter_init(g.filter); 51 | while(lnklist_iter_hasnext(g.filter)) { 52 | filter = lnklist_iter_next(g.filter); 53 | if(!filter->stat) { 54 | continue; 55 | } 56 | if(strisequal(method, "REFRESH")) { 57 | isrefresh = 1; 58 | } 59 | stat = filter->stat; 60 | snprintf(success, sizeof(success), "{\"total\":%u,\"dupsyn\":%u,\"dupsynack\":%u}", 61 | stat[0].success.total - (isrefresh ? stat[1].success.total : 0), 62 | stat[0].success.dupsyn - (isrefresh ? stat[1].success.dupsyn : 0), 63 | stat[0].success.dupsynack - (isrefresh ? stat[1].success.dupsynack : 0) 64 | ); 65 | if(strisequal(method, "REFRESH")) { 66 | snprintf(failure, sizeof(failure), "{\"total\":%u,\"timeout\":%u,\"reject\":%u}", 67 | stat[0].failure.total - (isrefresh ? stat[1].failure.total : 0), 68 | stat[0].failure.timeout - (isrefresh ? stat[1].failure.timeout : 0), 69 | (stat[0].failure.reject - (isrefresh ? stat[1].failure.reject : 0)) + (stat[0].failure.unreach - (isrefresh ? stat[1].failure.unreach : 0)) 70 | ); 71 | } 72 | else { 73 | snprintf(failure, sizeof(failure), "{\"total\":%u,\"timeout\":%u,\"reject\":%u, \"unreach\":%u}", 74 | stat[0].failure.total - (isrefresh ? stat[1].failure.total : 0), 75 | stat[0].failure.timeout - (isrefresh ? stat[1].failure.timeout : 0), 76 | stat[0].failure.reject - (isrefresh ? stat[1].failure.reject : 0), 77 | stat[0].failure.unreach - (isrefresh ? stat[1].failure.unreach : 0) 78 | ); 79 | } 80 | snprintf(buf, sizeof(buf), "%s{\"%s\":{\"success\":%s,\"failure\":%s}}", isfirst ? "" : ",", filter->name, success, failure); 81 | send(soc, buf, strlen(buf), 0); 82 | if(strisequal(method, "REFRESH")) { 83 | memcpy(&stat[1], &stat[0], sizeof(struct tcpeek_stat)); 84 | } 85 | if(isfirst) isfirst = 0; 86 | } 87 | if(!strisequal(method, "REFRESH")) { 88 | struct pcap_stat ps; 89 | memset(&ps, 0, sizeof(ps)); 90 | pcap_stats(g.pcap.pcap, &ps); 91 | snprintf(buf, sizeof(buf), ",{\"pcap\":{\"stats\":{\"recv\":%u,\"drop\":%u,\"ifdrop\":%u}}}", ps.ps_recv, ps.ps_drop, ps.ps_ifdrop); 92 | send(soc, buf, strlen(buf), 0); 93 | } 94 | send(soc, "]", 1, 0); 95 | pthread_mutex_unlock(&g.session.mutex); 96 | } 97 | -------------------------------------------------------------------------------- /src/lnklist.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lnklist.h" 4 | 5 | struct lnklist_node { 6 | struct lnklist_node *next; 7 | struct lnklist_node *prev; 8 | void *data; 9 | }; 10 | 11 | struct lnklist { 12 | struct lnklist_node *head; 13 | struct lnklist_node *tail; 14 | struct lnklist_node *iter; 15 | int size; 16 | }; 17 | 18 | static const struct lnklist_node LNKLIST_ITER_UNDERFLOW = {NULL, NULL, NULL}; 19 | 20 | #ifdef __LNKLIST_DEBUG__ 21 | int 22 | main(int argc, char *argv[]) { 23 | struct lnklist *list; 24 | int count; 25 | 26 | list = lnklist_create(); 27 | for(count = 0; count < 10000; count++) { 28 | lnklist_add(list, malloc(100), lnklist_size(list)); 29 | } 30 | for(count = 1000; count < 2000; count++) { 31 | lnklist_add(list, malloc(100), lnklist_size(list)); 32 | } 33 | for(count = 3000; count < 4000; count++) { 34 | lnklist_add(list, malloc(100), lnklist_size(list)); 35 | } 36 | for(count = 7000; count < 8000; count++) { 37 | lnklist_add(list, malloc(100), lnklist_size(list)); 38 | } 39 | for(count = 10000; count < 11000; count++) { 40 | lnklist_add(list, malloc(100), lnklist_size(list)); 41 | } 42 | for(count = 14000; count < 15000; count++) { 43 | lnklist_add(list, malloc(100), lnklist_size(list)); 44 | } 45 | fprintf(stderr, "lnklist_size(): %zd\n", lnklist_size(list)); 46 | lnklist_iter_init(list); 47 | while(lnklist_iter_hasnext(list)) { 48 | free(lnklist_iter_remove_next(list)); 49 | } 50 | fprintf(stderr, "lnklist_size(): %zd\n", lnklist_size(list)); 51 | lnklist_destroy(list); 52 | return 0; 53 | } 54 | #endif 55 | 56 | struct lnklist * 57 | lnklist_create(void) { 58 | struct lnklist *obj; 59 | 60 | obj = (struct lnklist *)malloc(sizeof(struct lnklist)); 61 | if(obj) { 62 | obj->head = NULL; 63 | obj->tail = NULL; 64 | obj->iter = (struct lnklist_node *)&LNKLIST_ITER_UNDERFLOW; 65 | obj->size = 0; 66 | } 67 | return obj; 68 | } 69 | 70 | void 71 | lnklist_destroy(struct lnklist *obj) { 72 | lnklist_destroy_with_destructor(obj, NULL); 73 | } 74 | 75 | void 76 | lnklist_destroy_with_destructor(struct lnklist *obj, void (*destructor)(void *)) { 77 | lnklist_iter_init(obj); 78 | while(lnklist_iter_hasnext(obj)) { 79 | destructor ? destructor(lnklist_iter_remove_next(obj)) : lnklist_iter_remove_next(obj); 80 | } 81 | free(obj); 82 | } 83 | 84 | ssize_t 85 | lnklist_size(struct lnklist *obj) { 86 | return obj ? obj->size : -1; 87 | } 88 | 89 | void * 90 | lnklist_add(struct lnklist *obj, void *data, int index) { 91 | struct lnklist_node *node, *ptr; 92 | int offset; 93 | 94 | if(!obj || obj->size < index) { 95 | return NULL; 96 | } 97 | node = (struct lnklist_node *)malloc(sizeof(struct lnklist_node)); 98 | if(!node) { 99 | return NULL; 100 | } 101 | node->data = data; 102 | if(index == 0 || index < obj->size / 2) { 103 | ptr = obj->head; 104 | for(offset = 0; offset < index; offset++) { 105 | ptr = ptr->next; 106 | } 107 | node->prev = ptr ? ptr->prev : NULL; 108 | node->next = ptr; 109 | } 110 | else { 111 | ptr = obj->tail; 112 | for(offset = obj->size - 1; offset > index; offset--) { 113 | ptr = ptr->prev; 114 | } 115 | node->prev = ptr; 116 | node->next = ptr ? ptr->next : NULL; 117 | } 118 | if(node->next) { 119 | node->next->prev = node; 120 | } 121 | else { 122 | obj->tail = node; 123 | } 124 | if(node->prev) { 125 | node->prev->next = node; 126 | } 127 | else { 128 | obj->head = node; 129 | } 130 | obj->size++; 131 | return node->data; 132 | } 133 | 134 | void * 135 | lnklist_add_tail(struct lnklist *obj, void *data) { 136 | return obj ? lnklist_add(obj, data, obj->size) : NULL; 137 | } 138 | 139 | void * 140 | lnklist_remove(struct lnklist *obj, int index) { 141 | struct lnklist_node *ptr; 142 | int offset; 143 | void *data; 144 | 145 | if(!obj || obj->size <= index) { 146 | return NULL; 147 | } 148 | if(index < obj->size / 2) { 149 | ptr = obj->head; 150 | for(offset = 0; offset < index; offset++) { 151 | ptr = ptr->next; 152 | } 153 | } 154 | else { 155 | ptr = obj->tail; 156 | for(offset = obj->size - 1; offset > index; offset--) { 157 | ptr = ptr->prev; 158 | } 159 | } 160 | if(ptr->prev) { 161 | ptr->prev->next = ptr->next; 162 | } 163 | else { 164 | obj->head = ptr->next; 165 | if(obj->head) { 166 | obj->head->prev = NULL; 167 | } 168 | } 169 | if(ptr->next) { 170 | ptr->next->prev = ptr->prev; 171 | } 172 | else { 173 | obj->tail = ptr->prev; 174 | if(obj->tail) { 175 | obj->tail->next = NULL; 176 | } 177 | } 178 | data = ptr->data; 179 | free(ptr); 180 | obj->size--; 181 | return data; 182 | } 183 | 184 | void * 185 | lnklist_get(struct lnklist *obj, int index) { 186 | struct lnklist_node *ptr; 187 | int offset; 188 | 189 | if(!obj || obj->size <= index) { 190 | return NULL; 191 | } 192 | if(index < obj->size / 2) { 193 | ptr = obj->head; 194 | for(offset = 0; offset < index; offset++) { 195 | ptr = ptr->next; 196 | } 197 | } 198 | else { 199 | ptr = obj->tail; 200 | for(offset = obj->size - 1; offset > index; offset--) { 201 | ptr = ptr->prev; 202 | } 203 | } 204 | return ptr->data; 205 | } 206 | 207 | void 208 | lnklist_iter_init(struct lnklist *obj) { 209 | if(obj) { 210 | obj->iter = (struct lnklist_node *)&LNKLIST_ITER_UNDERFLOW; 211 | } 212 | } 213 | 214 | int 215 | lnklist_iter_hasnext(struct lnklist *obj) { 216 | struct lnklist_node *next; 217 | 218 | if(!obj || !obj->iter) { 219 | return 0; 220 | } 221 | next = (obj->iter == &LNKLIST_ITER_UNDERFLOW) ? obj->head : obj->iter->next; 222 | return next ? 1 : 0; 223 | } 224 | 225 | void * 226 | lnklist_iter_next(struct lnklist *obj) { 227 | if(!obj || !obj->iter) { 228 | return NULL; 229 | } 230 | obj->iter = (obj->iter == &LNKLIST_ITER_UNDERFLOW) ? obj->head : obj->iter->next; 231 | return obj->iter ? obj->iter->data : NULL; 232 | } 233 | 234 | void * 235 | lnklist_iter_remove(struct lnklist *obj) { 236 | struct lnklist_node *ptr; 237 | void *data; 238 | 239 | if(!obj || !obj->iter || obj->iter == &LNKLIST_ITER_UNDERFLOW) { 240 | return NULL; 241 | } 242 | ptr = obj->iter; 243 | if(ptr->prev) { 244 | ptr->prev->next = ptr->next; 245 | obj->iter = ptr->prev; 246 | } 247 | else { 248 | obj->head = ptr->next; 249 | if(obj->head) { 250 | obj->head->prev = NULL; 251 | } 252 | obj->iter = (struct lnklist_node *)&LNKLIST_ITER_UNDERFLOW; 253 | } 254 | if(ptr->next) { 255 | ptr->next->prev = ptr->prev; 256 | } 257 | else { 258 | obj->tail = ptr->prev; 259 | if(obj->tail) { 260 | obj->tail->next = NULL; 261 | } 262 | } 263 | data = ptr->data; 264 | free(ptr); 265 | obj->size--; 266 | return data; 267 | } 268 | 269 | void * 270 | lnklist_iter_remove_next(struct lnklist *obj) { 271 | struct lnklist_node *ptr; 272 | void *data; 273 | 274 | if(!obj || !obj->iter) { 275 | return NULL; 276 | } 277 | ptr = (obj->iter == &LNKLIST_ITER_UNDERFLOW) ? obj->head : obj->iter->next; 278 | if(!ptr) { 279 | return NULL; 280 | } 281 | if(ptr->prev) { 282 | ptr->prev->next = ptr->next; 283 | } 284 | else { 285 | obj->head = ptr->next; 286 | if(obj->head) { 287 | obj->head->prev = NULL; 288 | } 289 | } 290 | if(ptr->next) { 291 | ptr->next->prev = ptr->prev; 292 | } 293 | else { 294 | obj->tail = ptr->prev; 295 | if(obj->tail) { 296 | obj->tail->next = NULL; 297 | } 298 | } 299 | data = ptr->data; 300 | free(ptr); 301 | obj->size--; 302 | return data; 303 | } 304 | -------------------------------------------------------------------------------- /src/lnklist.h: -------------------------------------------------------------------------------- 1 | #ifndef __LNKLIST_H__ 2 | #define __LNKLIST_H__ 3 | 4 | #include 5 | #include 6 | 7 | struct lnklist; 8 | 9 | extern struct lnklist * 10 | lnklist_create(void); 11 | extern void 12 | lnklist_destroy(struct lnklist *obj); 13 | extern void 14 | lnklist_destroy_with_destructor(struct lnklist *obj, void (*destructor)(void *)); 15 | extern ssize_t 16 | lnklist_size(struct lnklist *obj); 17 | extern void * 18 | lnklist_add(struct lnklist *obj, void *data, int index); 19 | extern void * 20 | lnklist_add_tail(struct lnklist *obj, void *data); 21 | extern void * 22 | lnklist_remove(struct lnklist *obj, int index); 23 | extern void * 24 | lnklist_get(struct lnklist *obj, int index); 25 | extern void 26 | lnklist_iter_init(struct lnklist *obj); 27 | extern int 28 | lnklist_iter_hasnext(struct lnklist *obj); 29 | extern void * 30 | lnklist_iter_next(struct lnklist *obj); 31 | extern void * 32 | lnklist_iter_remove(struct lnklist *obj); 33 | extern void * 34 | lnklist_iter_remove_next(struct lnklist *obj); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/session.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | static struct tcpeek_session * 4 | tcpeek_session_create(void); 5 | static int 6 | tcpeek_session_isowner(struct tcpeek_session *session, struct tcpeek_segment *segment); 7 | 8 | static struct tcpeek_session * 9 | tcpeek_session_create(void) { 10 | struct tcpeek_session *session; 11 | 12 | session = (struct tcpeek_session *)malloc(sizeof(struct tcpeek_session)); 13 | if(session) { 14 | memset(session, 0x00, sizeof(struct tcpeek_session)); 15 | session->sequence.state[0] = TCPEEK_TCP_CLOSED; 16 | session->sequence.state[1] = TCPEEK_TCP_CLOSED; 17 | } 18 | return session; 19 | } 20 | 21 | void 22 | tcpeek_session_destroy(struct tcpeek_session *session) { 23 | if(session) { 24 | lnklist_destroy(session->stat); 25 | free(session); 26 | } 27 | } 28 | 29 | struct tcpeek_session * 30 | tcpeek_session_get(struct tcpeek_segment *segment) { 31 | struct tcpeek_session_key key; 32 | struct tcpeek_session *session; 33 | 34 | key.addr[0] = segment->tcp.ip.hdr.ip_src.s_addr; 35 | key.addr[1] = segment->tcp.ip.hdr.ip_dst.s_addr; 36 | key.port[0] = segment->tcp.hdr.th_sport; 37 | key.port[1] = segment->tcp.hdr.th_dport; 38 | session = hashtable_get(g.session.table, &key, sizeof(key)); 39 | if(!session) { 40 | key.addr[0] = segment->tcp.ip.hdr.ip_dst.s_addr; 41 | key.addr[1] = segment->tcp.ip.hdr.ip_src.s_addr; 42 | key.port[0] = segment->tcp.hdr.th_dport; 43 | key.port[1] = segment->tcp.hdr.th_sport; 44 | session = hashtable_get(g.session.table, &key, sizeof(key)); 45 | } 46 | return session; 47 | } 48 | 49 | struct tcpeek_session * 50 | tcpeek_session_open(struct tcpeek_segment *segment, struct lnklist *stat) { 51 | struct tcpeek_session *session; 52 | 53 | session = tcpeek_session_create(); 54 | if(!session) { 55 | return NULL; 56 | } 57 | session->key.addr[0] = segment->tcp.ip.hdr.ip_src.s_addr; 58 | session->key.addr[1] = segment->tcp.ip.hdr.ip_dst.s_addr; 59 | session->key.port[0] = segment->tcp.hdr.th_sport; 60 | session->key.port[1] = segment->tcp.hdr.th_dport; 61 | session->sequence.timestamp[0] = segment->timestamp; 62 | session->sequence.timestamp[1] = segment->timestamp; 63 | session->stat = stat; 64 | return hashtable_put(g.session.table, &session->key, sizeof(session->key), session); 65 | } 66 | 67 | void 68 | tcpeek_session_close(struct tcpeek_session *session) { 69 | struct tcpeek_stat *stat; 70 | 71 | lnklist_iter_init(session->stat); 72 | while(lnklist_iter_hasnext(session->stat)) { 73 | stat = lnklist_iter_next(session->stat); 74 | if(session->failure) { 75 | stat->failure.total++; 76 | if(session->failure == TCPEEK_SESSION_FAILURE_TIMEOUT) { 77 | stat->failure.timeout++; 78 | } 79 | else if(session->failure == TCPEEK_SESSION_FAILURE_REJECT) { 80 | stat->failure.reject++; 81 | } 82 | else if(session->failure == TCPEEK_SESSION_FAILURE_UNREACH) { 83 | stat->failure.unreach++; 84 | } 85 | } 86 | else { 87 | stat->success.total++; 88 | if(session->counter.dupsyn) { 89 | stat->success.dupsyn++; 90 | } 91 | if(session->counter.dupsynack) { 92 | stat->success.dupsynack++; 93 | } 94 | if(session->counter.dupack) { 95 | stat->success.dupack++; 96 | } 97 | } 98 | } 99 | if (!g.option.quiet) { 100 | tcpeek_session_print(session); 101 | } 102 | hashtable_remove(g.session.table, &session->key, sizeof(session->key)); 103 | tcpeek_session_destroy(session); 104 | } 105 | 106 | void 107 | tcpeek_session_timeout(struct tcpeek_session *session) { 108 | session->failure = TCPEEK_SESSION_FAILURE_TIMEOUT; 109 | gettimeofday(&session->sequence.timestamp[1], NULL); 110 | tcpeek_session_close(session); 111 | } 112 | 113 | int 114 | tcpeek_session_isestablished(struct tcpeek_session *session) { 115 | return (session->sequence.state[0] == TCPEEK_TCP_ESTABLISHED && session->sequence.state[1] == TCPEEK_TCP_ESTABLISHED) ? 1 : 0; 116 | } 117 | 118 | int 119 | tcpeek_session_isclosed(struct tcpeek_session *session) { 120 | return (session->sequence.state[0] == TCPEEK_TCP_CLOSED && session->sequence.state[1] == TCPEEK_TCP_CLOSED) ? 1 : 0; 121 | } 122 | 123 | int 124 | tcpeek_session_istimeout(struct tcpeek_session *session) { 125 | struct timeval now, difftime; 126 | 127 | gettimeofday(&now, NULL); 128 | tvsub(&now, &session->sequence.timestamp[1], &difftime); 129 | return (difftime.tv_sec >= g.option.timeout) ? 1 : 0; 130 | } 131 | 132 | static int 133 | tcpeek_session_isowner(struct tcpeek_session *session, struct tcpeek_segment *segment) { 134 | return session->key.addr[0] == segment->tcp.ip.hdr.ip_src.s_addr ? 1 : 0; 135 | } 136 | 137 | int 138 | tcpeek_session_recv_syn(struct tcpeek_session *session, struct tcpeek_segment *segment) { 139 | int self, peer; 140 | 141 | peer = (self = tcpeek_session_isowner(session, segment) ^ 0x01) ^ 0x01; 142 | session->sequence.timestamp[1] = segment->timestamp; 143 | switch(session->sequence.state[self]) { 144 | case TCPEEK_TCP_CLOSED: 145 | session->sequence.state[self] = TCPEEK_TCP_SYN_SENT; 146 | session->sequence.state[peer] = TCPEEK_TCP_LISTEN; 147 | session->sequence.fseq[self] = session->sequence.lseq[self] = ntohl(segment->tcp.hdr.th_seq); 148 | session->sequence.fack[self] = session->sequence.lack[self] = ntohl(segment->tcp.hdr.th_ack); 149 | break; 150 | case TCPEEK_TCP_LISTEN: /* simultaneous open */ 151 | if(!(ntohl(segment->tcp.hdr.th_ack) == session->sequence.lseq[peer] + 1)) { 152 | lprintf(LOG_WARNING, "Duplicate connection."); 153 | return -1; 154 | } 155 | session->sequence.state[self] = TCPEEK_TCP_SYN_SENT; 156 | session->sequence.fseq[self] = session->sequence.lseq[self] = ntohl(segment->tcp.hdr.th_seq); 157 | session->sequence.fack[self] = session->sequence.lack[self] = ntohl(segment->tcp.hdr.th_ack); 158 | break; 159 | case TCPEEK_TCP_SYN_SENT: /* retransmit */ 160 | if(!(ntohl(segment->tcp.hdr.th_seq) == session->sequence.fseq[self] && ntohl(segment->tcp.hdr.th_ack) == session->sequence.fack[self])) { 161 | lprintf(LOG_WARNING, "Duplicate connection."); 162 | return -1; 163 | } 164 | session->counter.dupsyn++; 165 | break; 166 | default: 167 | lprintf(LOG_WARNING, "Duplicate connection."); 168 | return -1; 169 | } 170 | return 0; 171 | } 172 | 173 | int 174 | tcpeek_session_recv_synack(struct tcpeek_session *session, struct tcpeek_segment *segment) { 175 | int self, peer; 176 | char msg[128]; 177 | 178 | peer = (self = tcpeek_session_isowner(session, segment) ^ 0x01) ^ 0x01; 179 | session->sequence.timestamp[1] = segment->timestamp; 180 | switch(session->sequence.state[self]) { 181 | case TCPEEK_TCP_LISTEN: 182 | if(!(ntohl(segment->tcp.hdr.th_ack) == session->sequence.lseq[peer] + 1)) { 183 | snprintf(msg, sizeof(msg), "%s: sequence error. %d/%d", __func__, session->sequence.state[self], session->sequence.state[peer]); 184 | tcpeek_print_segment(segment, self, msg); 185 | return -1; 186 | } 187 | session->sequence.state[self] = TCPEEK_TCP_SYN_RECV; 188 | session->sequence.fseq[self] = session->sequence.lseq[self] = ntohl(segment->tcp.hdr.th_seq); 189 | session->sequence.fack[self] = session->sequence.lack[self] = ntohl(segment->tcp.hdr.th_ack); 190 | break; 191 | case TCPEEK_TCP_SYN_SENT: /* simultaneous open */ 192 | if(!(ntohl(segment->tcp.hdr.th_seq) == session->sequence.lseq[self] && ntohl(segment->tcp.hdr.th_ack) == session->sequence.lseq[peer] + 1)) { 193 | snprintf(msg, sizeof(msg), "%s: sequence error. %d/%d", __func__, session->sequence.state[self], session->sequence.state[peer]); 194 | tcpeek_print_segment(segment, self, msg); 195 | return -1; 196 | } 197 | session->sequence.state[self] = TCPEEK_TCP_SYN_RECV; 198 | session->sequence.lack[self] = ntohl(segment->tcp.hdr.th_ack); 199 | break; 200 | case TCPEEK_TCP_SYN_RECV: /* retransmit */ 201 | if(!(ntohl(segment->tcp.hdr.th_seq) == session->sequence.lseq[self] && ntohl(segment->tcp.hdr.th_ack) == session->sequence.lack[self])) { 202 | snprintf(msg, sizeof(msg), "%s: sequence error. %d/%d", __func__, session->sequence.state[self], session->sequence.state[peer]); 203 | tcpeek_print_segment(segment, self, msg); 204 | return -1; 205 | } 206 | session->counter.dupsynack++; 207 | break; 208 | default: 209 | snprintf(msg, sizeof(msg), "%s: sequence error. %d/%d", __func__, session->sequence.state[self], session->sequence.state[peer]); 210 | tcpeek_print_segment(segment, self, msg); 211 | return -1; 212 | } 213 | return 0; 214 | } 215 | 216 | int 217 | tcpeek_session_recv_ack(struct tcpeek_session *session, struct tcpeek_segment *segment) { 218 | int self, peer; 219 | char msg[128]; 220 | 221 | peer = (self = tcpeek_session_isowner(session, segment) ^ 0x01) ^ 0x01; 222 | session->sequence.timestamp[1] = segment->timestamp; 223 | switch(session->sequence.state[self]) { 224 | case TCPEEK_TCP_SYN_SENT: 225 | if(ntohl(segment->tcp.hdr.th_seq) == session->sequence.lseq[self] && ntohl(segment->tcp.hdr.th_ack) == session->sequence.lseq[peer] + 1) { 226 | snprintf(msg, sizeof(msg), "%s: sequence error. %d/%d", __func__, session->sequence.state[self], session->sequence.state[peer]); 227 | tcpeek_print_segment(segment, self, msg); 228 | return -1; 229 | } 230 | session->sequence.state[self] = session->sequence.state[peer] = TCPEEK_TCP_ESTABLISHED; 231 | session->sequence.lack[self] = ntohl(segment->tcp.hdr.th_ack); 232 | break; 233 | case TCPEEK_TCP_SYN_RECV: 234 | if(ntohl(segment->tcp.hdr.th_seq) == session->sequence.lseq[self] + 1 && ntohl(segment->tcp.hdr.th_ack) == session->sequence.lseq[peer] + 1) { 235 | snprintf(msg, sizeof(msg), "%s: sequence error. %d/%d", __func__, session->sequence.state[self], session->sequence.state[peer]); 236 | tcpeek_print_segment(segment, self, msg); 237 | return -1; 238 | } 239 | session->sequence.state[self] = session->sequence.state[peer] = TCPEEK_TCP_ESTABLISHED; 240 | session->sequence.lseq[self] = ntohl(segment->tcp.hdr.th_seq); 241 | session->sequence.lack[self] = ntohl(segment->tcp.hdr.th_ack); 242 | break; 243 | case TCPEEK_TCP_ESTABLISHED: 244 | // TODO: 245 | break; 246 | default: 247 | snprintf(msg, sizeof(msg), "%s: sequence error. %d/%d", __func__, session->sequence.state[self], session->sequence.state[peer]); 248 | tcpeek_print_segment(segment, self, msg); 249 | return -1; 250 | } 251 | return 0; 252 | } 253 | 254 | int 255 | tcpeek_session_recv_rst(struct tcpeek_session *session, struct tcpeek_segment *segment) { 256 | int self, peer; 257 | 258 | peer = (self = tcpeek_session_isowner(session, segment) ^ 0x01) ^ 0x01; 259 | session->sequence.timestamp[1] = segment->timestamp; 260 | session->sequence.state[self] = TCPEEK_TCP_CLOSED; 261 | session->sequence.state[peer] = TCPEEK_TCP_CLOSED; 262 | session->failure = TCPEEK_SESSION_FAILURE_REJECT; 263 | return 0; 264 | } 265 | 266 | static char * 267 | tcpeek_session_result2str(int code) { 268 | switch(code) { 269 | case 0: 270 | return " success "; 271 | case TCPEEK_SESSION_FAILURE_TIMEOUT: 272 | return "failure (timeout)"; 273 | case TCPEEK_SESSION_FAILURE_REJECT: 274 | return "failure (reject) "; 275 | case TCPEEK_SESSION_FAILURE_UNREACH: 276 | return "failure (unreach)"; 277 | default: 278 | return "failure (unknown)"; 279 | } 280 | } 281 | 282 | void 283 | tcpeek_session_print(struct tcpeek_session *session) { 284 | static int firsttime = 1; 285 | struct tm tm; 286 | char timestamp[128]; 287 | char src[128], dst[128]; 288 | struct timeval difftime; 289 | 290 | if(firsttime && firsttime--) { 291 | lprintf(LOG_INFO, " TIME(s) | TIMESTAMP | SRC IP:PORT DST IP:PORT | RESULTS | DUP SYN DUP S/A "); 292 | lprintf(LOG_INFO, "----------------------------------------------------------------------------------------------------------------------"); 293 | } 294 | tvsub(&session->sequence.timestamp[1], &session->sequence.timestamp[0], &difftime); 295 | localtime_r(&session->sequence.timestamp[0].tv_sec, &tm); 296 | strftime(timestamp, sizeof(timestamp), "%y-%m-%d %T", &tm); 297 | lprintf(LOG_INFO, "%4d.%03d | %s.%03d | %15s:%-5u %15s:%-5u | %s | %7u %7u ", 298 | (int)(difftime.tv_sec), 299 | (int)(difftime.tv_usec / 1000), 300 | timestamp, 301 | (int)(session->sequence.timestamp[0].tv_usec / 1000), 302 | inet_ntop(AF_INET, &session->key.addr[0], src, sizeof(src)), 303 | ntohs(session->key.port[0]), 304 | inet_ntop(AF_INET, &session->key.addr[1], dst, sizeof(dst)), 305 | ntohs(session->key.port[1]), 306 | tcpeek_session_result2str(session->failure), 307 | session->counter.dupsyn, 308 | session->counter.dupsynack 309 | ); 310 | } 311 | -------------------------------------------------------------------------------- /src/tcpeek.c: -------------------------------------------------------------------------------- 1 | #include "tcpeek.h" 2 | 3 | static void 4 | tcpeek_execute(void); 5 | static void 6 | tcpeek_fetch(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *pktdata); 7 | static void 8 | tcpeek_terminate_session(void); 9 | static void 10 | tcpeek_print_pcap_status(void); 11 | static void 12 | tcpeek_print_summary(void); 13 | 14 | int 15 | main(int argc, char *argv[]) { 16 | tcpeek_init(argc, argv); 17 | tcpeek_execute(); 18 | tcpeek_terminate(0); 19 | return 0; /* does not reached */ 20 | } 21 | 22 | void 23 | tcpeek_signal_handler(int signo) { 24 | if(signo == SIGINT || signo == SIGTERM) { 25 | g.terminate = 1; 26 | if(g.pcap.pcap) { 27 | pcap_breakloop(g.pcap.pcap); /* signal safe */ 28 | } 29 | } 30 | } 31 | 32 | static void 33 | tcpeek_execute(void) { 34 | int err, ready; 35 | pthread_t checker, listener; 36 | struct pollfd pfds[1]; 37 | 38 | err = pthread_create(&checker, NULL, tcpeek_checker_thread, NULL); 39 | if(err) { 40 | error_abort("%s", strerror(err)); 41 | } 42 | err = pthread_create(&listener, NULL, tcpeek_listener_thread, NULL); 43 | if(err) { 44 | error_abort("%s", strerror(err)); 45 | } 46 | tcpeek_print_pcap_status(); 47 | gettimeofday(&g.session.timestamp, NULL); 48 | pfds[0].fd = pcap_fileno(g.pcap.pcap); 49 | pfds[0].events = POLLIN; 50 | while(!g.terminate) { 51 | if((ready = poll(pfds, sizeof(pfds) / sizeof(struct pollfd), 1000)) == -1) { 52 | if(errno != EINTR) { 53 | error_abort("%s", strerror(err)); 54 | } 55 | } 56 | else if(ready == 0) { 57 | /* timeout */ 58 | } 59 | else { 60 | if(pcap_dispatch(g.pcap.pcap, 0, tcpeek_fetch, NULL) == -1) { 61 | error_abort("%s", pcap_geterr(g.pcap.pcap)); 62 | } 63 | } 64 | } 65 | err = pthread_join(checker, NULL); 66 | if(err) { 67 | lprintf(LOG_WARNING, "%s", strerror(err)); 68 | } 69 | err = pthread_join(listener, NULL); 70 | if(err) { 71 | lprintf(LOG_WARNING, "%s", strerror(err)); 72 | } 73 | tcpeek_print_summary(); 74 | } 75 | 76 | static void 77 | tcpeek_fetch(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *pktdata) { 78 | struct tcpeek_segment segment; 79 | uint8_t *payload; 80 | struct tcpeek_session *session; 81 | struct lnklist *stat; 82 | 83 | memset(&segment, 0x00, sizeof(segment)); 84 | segment.timestamp = pkthdr->ts; 85 | payload = tcpeek_disassemble((uint8_t *)pktdata, (uint16_t)pkthdr->caplen, g.pcap.datalink, &segment); 86 | if(!payload) { 87 | return; 88 | } 89 | pthread_mutex_lock(&g.session.mutex); 90 | session = tcpeek_session_get(&segment); 91 | if(!session) { 92 | if(segment.tcp.hdr.th_flags != TH_SYN) { 93 | pthread_mutex_unlock(&g.session.mutex); 94 | return; 95 | } 96 | stat = tcpeek_filter_lookup(&segment); 97 | if(!stat) { 98 | pthread_mutex_unlock(&g.session.mutex); 99 | return; 100 | } 101 | session = tcpeek_session_open(&segment, stat); 102 | if(!session) { 103 | lprintf(LOG_WARNING, "%s", "session open error."); 104 | pthread_mutex_unlock(&g.session.mutex); 105 | return; 106 | } 107 | } 108 | if(segment.icmp_unreach) { 109 | session->failure = TCPEEK_SESSION_FAILURE_UNREACH; 110 | tcpeek_session_close(session); 111 | pthread_mutex_unlock(&g.session.mutex); 112 | return; 113 | } 114 | switch(segment.tcp.hdr.th_flags & (TH_SYN | TH_ACK | TH_RST | TH_FIN)) { 115 | case TH_SYN: 116 | tcpeek_session_recv_syn(session, &segment); 117 | break; 118 | case TH_SYN | TH_ACK: 119 | tcpeek_session_recv_synack(session, &segment); 120 | break; 121 | case TH_ACK: 122 | tcpeek_session_recv_ack(session, &segment); 123 | break; 124 | case TH_RST: 125 | case TH_RST | TH_ACK: 126 | tcpeek_session_recv_rst(session, &segment); 127 | break; 128 | default: 129 | break; 130 | } 131 | if(tcpeek_session_isestablished(session) || tcpeek_session_isclosed(session)) { 132 | tcpeek_session_close(session); 133 | } 134 | pthread_mutex_unlock(&g.session.mutex); 135 | } 136 | 137 | void 138 | tcpeek_terminate(int status) { 139 | if(g.option.expression) { 140 | lnklist_destroy_with_destructor(g.option.expression, free); 141 | } 142 | if(g.pcap.pcap) { 143 | pcap_close(g.pcap.pcap); 144 | } 145 | if(g.session.table) { 146 | tcpeek_terminate_session(); 147 | } 148 | if(g.filter) { 149 | lnklist_destroy_with_destructor(g.filter, (void (*)(void *))tcpeek_filter_destroy); 150 | } 151 | if(g.soc != -1) { 152 | close(g.soc); 153 | unlink(g.option.socket); 154 | } 155 | exit(status); 156 | } 157 | 158 | static void 159 | tcpeek_terminate_session(void) { 160 | struct lnklist *keys; 161 | struct hashtable_key *key; 162 | struct tcpeek_session *session; 163 | 164 | keys = hashtable_get_keys(g.session.table); 165 | lnklist_iter_init(keys); 166 | while(lnklist_iter_hasnext(keys)) { 167 | key = lnklist_iter_remove_next(keys); 168 | session = hashtable_get(g.session.table, hashtable_key_get_key(key), hashtable_key_get_len(key)); 169 | tcpeek_session_close(session); 170 | } 171 | lnklist_destroy(keys); 172 | hashtable_destroy(g.session.table); 173 | } 174 | 175 | static void 176 | tcpeek_print_pcap_status(void) { 177 | lprintf(LOG_INFO, "listening on %s, link-type %s (%s), capture size %d bytes, buffer size %d MB", 178 | g.option.ifname, 179 | pcap_datalink_val_to_name(g.pcap.datalink), 180 | pcap_datalink_val_to_description(g.pcap.datalink), 181 | g.pcap.snapshot, 182 | g.option.buffer 183 | ); 184 | } 185 | 186 | static void 187 | tcpeek_print_summary(void) { 188 | struct timeval now, difftime; 189 | struct tm tm; 190 | char from[128], to[128]; 191 | struct tcpeek_filter *filter; 192 | struct tcpeek_stat *stat; 193 | struct pcap_stat ps; 194 | 195 | memset(&ps, 0, sizeof(ps)); 196 | pcap_stats(g.pcap.pcap, &ps); 197 | gettimeofday(&now, NULL); 198 | strftime(from, sizeof(from), "%Y-%m-%d %T", localtime_r(&g.session.timestamp.tv_sec, &tm)); 199 | strftime(to, sizeof(to), "%Y-%m-%d %T", localtime_r(&now.tv_sec, &tm)); 200 | tvsub(&now, &g.session.timestamp, &difftime); 201 | lprintf(LOG_INFO, ""); 202 | lprintf(LOG_INFO, "========== TCPEEK SUMMARY =========="); 203 | lprintf(LOG_INFO, " from : %s", from); 204 | lprintf(LOG_INFO, " to : %s", to); 205 | lprintf(LOG_INFO, " time : %9d.%03d (sec)", (int)difftime.tv_sec, (int)(difftime.tv_usec / 1000)); 206 | lnklist_iter_init(g.filter); 207 | while(lnklist_iter_hasnext(g.filter)) { 208 | filter = lnklist_iter_next(g.filter); 209 | if(!(stat = filter->stat)) { 210 | continue; 211 | } 212 | lprintf(LOG_INFO, "------------------------------------"); 213 | lprintf(LOG_INFO, " %s", filter->name); 214 | lprintf(LOG_INFO, " Success: %d session", stat[0].success.total); 215 | lprintf(LOG_INFO, " SYN Segment Duplicate : %6d", stat[0].success.dupsyn); 216 | lprintf(LOG_INFO, " S/A Segment Duplicate : %6d", stat[0].success.dupsynack); 217 | lprintf(LOG_INFO, " Failure: %d session", stat[0].failure.total); 218 | lprintf(LOG_INFO, " Connection Timed Out : %6d", stat[0].failure.timeout); 219 | lprintf(LOG_INFO, " Connection Rejected : %6d", stat[0].failure.reject); 220 | if(g.option.icmp) { 221 | lprintf(LOG_INFO, " ICMP Unreachable : %6d", stat[0].failure.unreach); 222 | } 223 | } 224 | lprintf(LOG_INFO, "------------------------------------"); 225 | lprintf(LOG_INFO, " pcap stats"); 226 | lprintf(LOG_INFO, " recv : %u", ps.ps_recv); 227 | lprintf(LOG_INFO, " drop : %u", ps.ps_drop); 228 | lprintf(LOG_INFO, " ifdrop : %u", ps.ps_ifdrop); 229 | lprintf(LOG_INFO, "===================================="); 230 | } 231 | 232 | void 233 | tcpeek_print_segment(struct tcpeek_segment *segment, int pos, const char *msg) { 234 | struct tcphdr *tcphdr; 235 | struct ip *ip; 236 | char saddr[INET_ADDRSTRLEN], daddr[INET_ADDRSTRLEN]; 237 | 238 | tcphdr = &segment->tcp.hdr; 239 | ip = &segment->tcp.ip.hdr; 240 | lprintf(LOG_DEBUG, "TCP/IP %s:%u %s%c%c%c%c%c%c%s %s:%u (%08X / %08X) %d | %s", 241 | inet_ntop(AF_INET, pos == 0 ? &ip->ip_src : &ip->ip_dst, saddr, sizeof(saddr)), 242 | ntohs(pos == 0 ? tcphdr->th_sport : tcphdr->th_dport), 243 | pos == 0 ? " " : "<", 244 | tcphdr->th_flags & TH_URG ? 'U' : '-', 245 | tcphdr->th_flags & TH_ACK ? 'A' : '-', 246 | tcphdr->th_flags & TH_PUSH ? 'P' : '-', 247 | tcphdr->th_flags & TH_RST ? 'R' : '-', 248 | tcphdr->th_flags & TH_SYN ? 'S' : '-', 249 | tcphdr->th_flags & TH_FIN ? 'F' : '-', 250 | pos == 0 ? ">" : " ", 251 | inet_ntop(AF_INET, pos == 0 ? &ip->ip_dst : &ip->ip_src, daddr, sizeof(daddr)), 252 | ntohs(pos == 0 ? tcphdr->th_dport : tcphdr->th_sport), 253 | ntohl(tcphdr->th_seq), 254 | ntohl(tcphdr->th_ack), 255 | segment->tcp.psize, 256 | msg ? msg : ""); 257 | } 258 | -------------------------------------------------------------------------------- /src/tcpeek.h: -------------------------------------------------------------------------------- 1 | #ifndef __TCPEEK_H__ 2 | #define __TCPEEK_H__ 1 3 | #ifdef HAVE_CONFIG_H 4 | #include "config.h" 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "lnklist.h" 37 | #include "hashtable.h" 38 | 39 | #define error_abort(fmt, ...) \ 40 | do { fprintf(stderr, "%s: [error] " fmt "\n", __func__, ## __VA_ARGS__); tcpeek_terminate(1); } while(0); 41 | 42 | #define lprintf(level, fmt, ...) \ 43 | fprintf(stderr, fmt "\n", ## __VA_ARGS__) 44 | //fprintf(stderr, "%s: " fmt "\n", __func__, ## __VA_ARGS__) 45 | 46 | #define TCPEEK_SOCKET_FILE "/var/run/tcpeek/tcpeek.sock" 47 | 48 | #define TCPEEK_SESSION_TABLE_SIZE 9973 49 | #define TCPEEK_CHECKER_INTERVAL_SEC 1 50 | 51 | #define TCPEEK_FILTER_DIR_RX 1 52 | #define TCPEEK_FILTER_DIR_TX 2 53 | 54 | #define TCPEEK_SESSION_FAILURE_TIMEOUT 1 55 | #define TCPEEK_SESSION_FAILURE_REJECT 2 56 | #define TCPEEK_SESSION_FAILURE_UNREACH 3 57 | 58 | #define TCPEEK_CKSUM_NONE 0x00 59 | #define TCPEEK_CKSUM_IP 0x01 60 | #define TCPEEK_CKSUM_TCP 0x02 61 | #define TCPEEK_CKSUM_BOTH 0x03 62 | 63 | enum { 64 | TCPEEK_TCP_CLOSED = 1, 65 | TCPEEK_TCP_LISTEN, 66 | TCPEEK_TCP_SYN_SENT, 67 | TCPEEK_TCP_SYN_RECV, 68 | TCPEEK_TCP_ESTABLISHED 69 | }; 70 | 71 | struct { 72 | struct { 73 | char user[128]; 74 | char ifname[IFNAMSIZ]; 75 | char socket[PATH_MAX]; 76 | int checksum; 77 | int timeout; 78 | int buffer; 79 | int loglevel; 80 | int quiet; 81 | int promisc; 82 | int icmp; 83 | struct lnklist *expression; 84 | } option; 85 | struct { 86 | struct in_addr unicast; 87 | } addr; 88 | struct { 89 | pcap_t *pcap; 90 | int datalink; 91 | int snapshot; 92 | } pcap; 93 | struct { 94 | pthread_mutex_t mutex; 95 | struct hashtable *table; 96 | struct timeval timestamp; 97 | } session; 98 | struct lnklist *filter; 99 | int soc; 100 | int terminate; 101 | } g; 102 | 103 | struct tcpeek_stat { 104 | struct { 105 | uint32_t total; 106 | uint32_t dupsyn; 107 | uint32_t dupsynack; 108 | uint32_t dupack; 109 | } success; 110 | struct { 111 | uint32_t total; 112 | uint32_t timeout; 113 | uint32_t reject; 114 | uint32_t unreach; 115 | } failure; 116 | }; 117 | 118 | struct tcpeek_filter { 119 | struct tcpeek_stat *stat; 120 | char name[128]; 121 | int dir; 122 | struct lnklist *rule; 123 | }; 124 | 125 | struct tcpeek_filter_rule { 126 | struct in_addr addr; 127 | uint8_t prefix; 128 | struct lnklist *port; 129 | }; 130 | 131 | struct tcpeek_session_key { 132 | uint32_t addr[2]; 133 | uint16_t port[2]; 134 | } __attribute__ ((__packed__)); 135 | 136 | struct tcpeek_session { 137 | struct tcpeek_session_key key; 138 | struct { 139 | uint32_t fseq[2]; 140 | uint32_t lseq[2]; 141 | uint32_t fack[2]; 142 | uint32_t lack[2]; 143 | uint8_t state[2]; 144 | struct timeval timestamp[2]; 145 | } sequence; 146 | struct { 147 | uint32_t dupsyn; 148 | uint32_t dupsynack; 149 | uint32_t dupack; 150 | } counter; 151 | uint16_t failure; 152 | struct lnklist *stat; 153 | }; 154 | 155 | struct tcpeek_segment_datalink { 156 | union { 157 | struct ether_header ether; 158 | struct sll_header sll; 159 | } hdr; 160 | }; 161 | 162 | struct tcpeek_segment_ip { 163 | struct tcpeek_segment_datalink datalink; 164 | struct ip hdr; 165 | uint8_t opt[40]; 166 | }; 167 | 168 | struct tcpeek_segment_tcp { 169 | struct tcpeek_segment_ip ip; 170 | struct tcphdr hdr; 171 | uint8_t opt[40]; 172 | uint16_t psize; 173 | }; 174 | 175 | struct tcpeek_segment { 176 | struct timeval timestamp; 177 | int icmp_unreach; 178 | struct tcpeek_segment_tcp tcp; 179 | }; 180 | 181 | // tcpeek.c 182 | extern void 183 | tcpeek_signal_handler(int signo); 184 | extern void 185 | tcpeek_terminate(int status); 186 | extern void 187 | tcpeek_print_segment(struct tcpeek_segment *segment, int pos, const char *msg); 188 | 189 | // init.c 190 | extern void 191 | tcpeek_init(int argc, char *argv[]); 192 | 193 | // disassemble.c 194 | extern uint8_t * 195 | tcpeek_disassemble(const uint8_t *data, uint16_t size, int datalink, struct tcpeek_segment *dst); 196 | 197 | // filter.c 198 | extern struct tcpeek_filter * 199 | tcpeek_filter_create(void); 200 | extern void 201 | tcpeek_filter_destroy(struct tcpeek_filter *filter); 202 | extern struct tcpeek_filter_rule * 203 | tcpeek_filter_rule_create(void); 204 | extern void 205 | tcpeek_filter_rule_destroy(struct tcpeek_filter_rule *rule); 206 | extern int 207 | tcpeek_filter_parse(struct tcpeek_filter *filter, const char *expression); 208 | extern struct lnklist * 209 | tcpeek_filter_lookup(struct tcpeek_segment *segment); 210 | 211 | // session.c 212 | extern void 213 | tcpeek_session_destroy(struct tcpeek_session *session); 214 | extern struct tcpeek_session * 215 | tcpeek_session_get(struct tcpeek_segment *segment); 216 | extern struct tcpeek_session * 217 | tcpeek_session_open(struct tcpeek_segment *segment, struct lnklist *stats); 218 | extern void 219 | tcpeek_session_close(struct tcpeek_session *session); 220 | extern void 221 | tcpeek_session_timeout(struct tcpeek_session *session); 222 | extern int 223 | tcpeek_session_isestablished(struct tcpeek_session *session); 224 | extern int 225 | tcpeek_session_isclosed(struct tcpeek_session *session); 226 | extern int 227 | tcpeek_session_istimeout(struct tcpeek_session *session); 228 | extern int 229 | tcpeek_session_recv_syn(struct tcpeek_session *session, struct tcpeek_segment *segment); 230 | extern int 231 | tcpeek_session_recv_synack(struct tcpeek_session *session, struct tcpeek_segment *segment); 232 | extern int 233 | tcpeek_session_recv_ack(struct tcpeek_session *session, struct tcpeek_segment *segment); 234 | extern int 235 | tcpeek_session_recv_rst(struct tcpeek_session *session, struct tcpeek_segment *segment); 236 | extern void 237 | tcpeek_session_print(struct tcpeek_session *session); 238 | 239 | // checker.c 240 | extern void * 241 | tcpeek_checker_thread(void *arg); 242 | 243 | // listener.c 244 | extern void * 245 | tcpeek_listener_thread(void *arg); 246 | 247 | // checksum.c 248 | extern uint16_t 249 | cksum16(uint16_t *data, uint16_t size, uint32_t init); 250 | 251 | // common.c 252 | extern int 253 | strisempty(const char *str); 254 | extern int 255 | strisequal(const char *str1, const char *str2); 256 | extern char * 257 | strtrim(char *str); 258 | extern int 259 | strisdigit(const char *str); 260 | extern struct lnklist * 261 | strsplit(const char *str, const char *sep, size_t num); 262 | #if !defined(__USE_XOPEN2K8) && !defined(__USE_GNU) 263 | extern char * 264 | strndup(const char *s1, size_t n); 265 | #endif 266 | extern void * 267 | memdup(const void *s, size_t n); 268 | extern struct timeval * 269 | tvsub(struct timeval *a, struct timeval *b, struct timeval *res); 270 | extern struct timeval * 271 | tvadd(struct timeval *a, struct timeval *b); 272 | extern ssize_t 273 | recvsz(int socket, void *buffer, size_t length, int flags, int timeout); 274 | extern ssize_t 275 | recvln(int socket, char *buffer, size_t length, int flags, int *fin, int timeout); 276 | 277 | #endif 278 | -------------------------------------------------------------------------------- /src/tcpeekstat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | import os 4 | import sys 5 | import argparse 6 | import socket 7 | import json 8 | 9 | socket_file = '/var/run/tcpeek/tcpeek.sock' 10 | gmetric = '/usr/bin/gmetric' 11 | use_gmetric = False 12 | use_json = False 13 | 14 | def init(): 15 | global use_gmetric, use_json, socket_file 16 | parser = argparse.ArgumentParser() 17 | exgroup = parser.add_mutually_exclusive_group() 18 | exgroup.add_argument('-g', '--gmetric', dest='use_gmetric', help='exec gmetric', action='store_true') 19 | exgroup.add_argument('-j', '--json', dest='use_json', help='print statistics with json format', action='store_true') 20 | parser.add_argument('-U', '--socket', help='unix domain socket for tcpeek (default: %(default)s)', default=socket_file) 21 | parser.add_argument('-v', '--version', help='show version', action='version') 22 | args = parser.parse_args() 23 | socket_file = args.socket 24 | use_gmetric = args.use_gmetric 25 | use_json = args.use_json 26 | 27 | def getstat(): 28 | msg = str() 29 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 30 | sock.settimeout(3) 31 | try: 32 | sock.connect(socket_file) 33 | if use_gmetric: 34 | sock.send("REFRESH\r\n") 35 | else: 36 | sock.send("GET\r\n") 37 | while 1: 38 | chunk = sock.recv(1024) 39 | if chunk == '': 40 | break 41 | msg = msg + chunk 42 | except socket.error, e: 43 | print "socket.error:", e 44 | sys.exit() 45 | finally: 46 | sock.close() 47 | return msg 48 | 49 | def exec_gmetric(stat): 50 | for _filter in stat: 51 | for name in _filter.keys(): 52 | for section in _filter[name].keys(): 53 | for item in _filter[name][section].keys(): 54 | if item == 'reject' or item == 'timeout' or item == 'dupsyn' or item == 'dupsynack': 55 | cmd = "%s -n %s -v %u -t uint32" % (gmetric, '_'.join(["tcpeek", name, section, item]), _filter[name][section][item]) 56 | print cmd 57 | os.system(cmd) 58 | 59 | def print_stat(stat): 60 | if use_json: 61 | print json.dumps(stat, separators=(',', ':')) 62 | else: 63 | print "====== TCPEEK SUMMARY ======" 64 | for _filter in stat: 65 | for name in _filter.keys(): 66 | print "----------------------------" 67 | print " %s" % (name) 68 | print "----------------------------" 69 | for section in _filter[name].keys(): 70 | print " %s" % (section) 71 | for item in _filter[name][section].keys(): 72 | print " %-10s %10s" % (item, _filter[name][section][item]) 73 | print "============================" 74 | 75 | def main(): 76 | init() 77 | msg = getstat() 78 | #print msg 79 | data = json.loads(msg) 80 | if use_gmetric: 81 | exec_gmetric(data) 82 | else: 83 | print_stat(data) 84 | 85 | if __name__ == "__main__": 86 | main() 87 | --------------------------------------------------------------------------------