ktcpdump2.c
最終更新:2010/1/12
001: /* 002: * ktcpdump2.c 003: * libpcap を使った TCP パケット解析サンプルコード 004: * ・コネクションごとに別ファイルにダンプする。 005: * ・起動後に SYN を検出したものだけ記録する。 006: * ・Ctrl-C で終了。 007: */ 008: 009: #define PROG_NAME "ktcpdump2" 010: 011: #include "mystab_pcap.h" 012: #include "apr_getopt.h" 013: #include "apr_network_io.h" 014: 015: /* デバッグフラグ D0 全パケットの表示等 */ 016: #define D0 0 017: 018: /* デバッグフラグ D1 全状態遷移の表示 */ 019: #define D1 0 020: 021: /* デバッグフラグ D2 オープンクローズの表示 */ 022: #define D2 1 023: 024: /* デバッグフラグ DD その他 */ 025: #define DD 0 026: 027: 028: /* コマンドラインオプション定義 */ 029: 030: static const apr_getopt_option_t opt_option[] = { 031: /* long-option, short-option, has-arg flag, description */ 032: { "port_number", 'p', TRUE, "[MUST] port_number" }, 033: { "interface", 'i', TRUE, "[MUST] network device to use" }, 034: { "log_dir", 'l', TRUE, "[MUST] log directory" }, 035: { "help", 'h', FALSE, "[OPTION] show help" }, /* -h or --help */ 036: 037: #if defined(WIN32) || defined(WINDOWS) || defined(MSVC) 038: 039: { "disp_interfaces", 'D', FALSE, 040: "[OPTION] displays avalable interfaces (Windows Only)" }, 041: 042: #endif 043: 044: { NULL, 0, 0, NULL }, /* SENTINEL */ 045: }; 046: 047: 048: 049: /* コネクションレコードテーブルのサイズのデフォルト値 (65536未満)*/ 050: #define CONREC_TAB_SIZE 100 051: 052: #if 0 053: /* 応答待ち TCP ペイロードを格納するバッファのサイズ */ 054: #define TCP_PAYLOAD_BUFFER_SIZE 3000 055: 056: #endif 057: 058: /* コネクションレコードの保持のタイムアウト秒 */ 059: #define TIME_OUT_SEC 600 060: 061: /* コネクションレコードテーブル */ 062: 063: typedef struct _st_my_conrec { 064: 065: /* ステータス(0で未使用) */ 066: apr_byte_t stat; 067: 068: /* コネクションID */ 069: apr_uint32_t id; 070: 071: /* コネクション名 XXX.XXX.XXX.XXX:XXXXX-XXX.XXX.XXX.XXX:XXXXX */ 072: char name[43]; 073: 074: /* クライアントIPアドレス */ 075: char c_ipaddr[16]; 076: /* クライアントポート番号 */ 077: apr_uint16_t c_port; 078: /* サーバIPアドレス */ 079: char s_ipaddr[16]; 080: /* サーバポート番号 */ 081: apr_uint16_t s_port; 082: 083: /* 開始日時 */ 084: apr_time_t start_time; 085: 086: /* パケットの発生した最終日時 */ 087: time_t last_time; 088: 089: /* メモリプール */ 090: apr_pool_t *pool; 091: 092: /* ログファイルのファイルポインタ */ 093: apr_file_t *fp; 094: 095: /* クライアントシーケンス番号 */ 096: apr_uint32_t c_seq_no; 097: /* サーバシーケンス番号 */ 098: apr_uint32_t s_seq_no; 099: 100: #if 0 101: /* サーバ応答待ちのクライアントデータのサイズ */ 102: apr_uint16_t c_data_size; 103: /* クライアント応答待ちのサーバデータのサイズ */ 104: apr_uint16_t s_data_size; 105: 106: /* サーバ応答待ちのクライアントデータ */ 107: apr_byte_t *c_data; 108: /* クライアント応答待ちのサーバデータ */ 109: apr_byte_t *s_data; 110: 111: #endif 112: 113: } my_conrec; 114: 115: 116: /* コネクションテーブルのステータス */ 117: 118: #define CLOSED 1 119: #define SYN1 2 120: #define SYN2 3 121: #define ESTAB 4 122: #define C_FIN1 5 123: #define S_FIN1 8 124: #define UNKNOWN 99 125: 126: static apr_uint16_t count_active_conrec_data = 0; 127: static apr_uint16_t count_id = 0; 128: 129: my_conrec conrec_tab[CONREC_TAB_SIZE]; 130: 131: /* 監視対象ポート */ 132: static apr_uint16_t target_port = 0; 133: 134: /* ローカルアドレス文字列 */ 135: static const char *local_addr = NULL; 136: 137: /* ログ保存ディレクトリ */ 138: static const char *log_dir = NULL; 139: 140: /* 観測者情報 */ 141: static char *watcher_str = NULL; 142: 143: 144: void my_conrec_close ( 145: my_conrec * cr_ptr 146: ) { 147: 148: if (cr_ptr->fp) { 149: apr_status_t rv = APR_SUCCESS; 150: char kbuf[32]; 151: 152: apr_file_printf(cr_ptr->fp, "0\r\n"); 153: 154: rv = apr_rfc822_date(kbuf, apr_time_now()); 155: if (rv == APR_SUCCESS) { 156: apr_file_printf(cr_ptr->fp, "End-Date: %s\r\n", kbuf); 157: } 158: apr_file_printf(cr_ptr->fp, "\r\n"); 159: apr_file_close(cr_ptr->fp); 160: cr_ptr->fp = NULL; 161: } 162: if (cr_ptr->pool) { 163: apr_pool_destroy(cr_ptr->pool); 164: cr_ptr->pool = NULL; 165: } 166: 167: memset(cr_ptr, 0, sizeof(my_conrec)); 168: 169: count_active_conrec_data--; 170: } 171: 172: /* 173: * 返り値: 174: * エラー時は NULL 175: */ 176: 177: my_conrec *my_conrec_get ( 178: const char *c_ipaddr 179: , apr_uint16_t c_port 180: , const char *s_ipaddr 181: , apr_uint16_t s_port 182: ) { 183: my_conrec *cr_ptr = NULL; 184: int i=0; 185: for (i=0; i<CONREC_TAB_SIZE; i++) { 186: cr_ptr = & conrec_tab[i]; 187: if (! cr_ptr->stat) { 188: continue; 189: } 190: if (time(NULL) - cr_ptr->last_time >= TIME_OUT_SEC) { 191: if(DD)puts("#Time UP!."); 192: my_conrec_close(cr_ptr); 193: continue; 194: } 195: if (!strcmp(c_ipaddr, cr_ptr->c_ipaddr) && c_port==cr_ptr->c_port && 196: !strcmp(s_ipaddr, cr_ptr->s_ipaddr) && s_port==cr_ptr->s_port) { 197: if(0)puts("Found!"); 198: return cr_ptr; 199: } 200: } 201: 202: return NULL; 203: } 204: 205: my_conrec * my_conrec_new ( 206: const char *c_ipaddr 207: , apr_uint16_t c_port 208: , const char *s_ipaddr 209: , apr_uint16_t s_port 210: ) { 211: my_conrec *cr_ptr = NULL; 212: int i=0; 213: for (i=0; i<CONREC_TAB_SIZE; i++) { 214: cr_ptr = & conrec_tab[i]; 215: if (cr_ptr->stat > 0) { 216: if (time(NULL) - cr_ptr->last_time < TIME_OUT_SEC) { 217: continue; 218: } else { 219: my_conrec_close(cr_ptr); 220: } 221: } 222: 223: cr_ptr->stat = CLOSED; 224: strcpy (cr_ptr->c_ipaddr, c_ipaddr); 225: cr_ptr->c_port = c_port; 226: strcpy (cr_ptr->s_ipaddr, s_ipaddr); 227: cr_ptr->s_port = s_port; 228: cr_ptr->start_time = apr_time_now(); 229: cr_ptr->last_time = time(NULL); 230: 231: snprintf(cr_ptr->name, 43, "%s:%d-%s:%d", c_ipaddr, c_port, s_ipaddr, 232: s_port); 233: 234: cr_ptr->id = count_id++; 235: 236: count_active_conrec_data++; 237: return cr_ptr; 238: } 239: 240: return NULL; 241: } 242: 243: void my_conrec_open ( 244: my_conrec * cr_ptr 245: ) { 246: apr_status_t rv = APR_SUCCESS; 247: rv = apr_pool_create(& cr_ptr->pool, NULL); 248: 249: if (rv != APR_SUCCESS) { 250: return; 251: } 252: 253: { 254: apr_time_exp_t tm; 255: char *out_filename = NULL; 256: char tbuf[16]; /* YYYYMMDD-hhmmss */ 257: int retsize = 0; 258: 259: apr_time_exp_lt(&tm, cr_ptr->start_time); 260: apr_strftime(tbuf, &retsize, 16, "%Y%m%d-%H%M%S", &tm); 261: 262: out_filename = apr_psprintf(cr_ptr->pool, 263: "%s/%s-%05u-%s-%u-%s-%u.txt", 264: log_dir, tbuf, cr_ptr->id, cr_ptr->c_ipaddr, cr_ptr->c_port, 265: cr_ptr->s_ipaddr, cr_ptr->s_port); 266: 267: if(DD)printf("#%s\n", out_filename); 268: 269: rv = apr_file_open(& cr_ptr->fp, out_filename, 270: APR_FOPEN_WRITE|APR_FOPEN_CREATE|APR_FOPEN_TRUNCATE|APR_FOPEN_BINARY, 271: APR_OS_DEFAULT, cr_ptr->pool); 272: 273: if (rv != APR_SUCCESS) { 274: if(DD)printf("#ERROR: %s (%u)\n", out_filename, cr_ptr->fp); 275: return; 276: } 277: 278: apr_file_printf(cr_ptr->fp, "Watcher: %s\r\n", watcher_str); 279: apr_file_printf(cr_ptr->fp, "Session-Id: %s-%05u\r\n", tbuf, cr_ptr->id); 280: apr_file_printf(cr_ptr->fp, "Client: %s:%u\r\n", cr_ptr->c_ipaddr, 281: cr_ptr->c_port); 282: apr_file_printf(cr_ptr->fp, "Server: %s:%u\r\n", cr_ptr->s_ipaddr, 283: cr_ptr->s_port); 284: { 285: char kbuf[32]; 286: rv = apr_rfc822_date(kbuf, cr_ptr->start_time); 287: if (rv == APR_SUCCESS) { 288: apr_file_printf(cr_ptr->fp, "Date: %s\r\n", kbuf); 289: } 290: apr_file_printf(cr_ptr->fp, "\r\n"); 291: } 292: } 293: } 294: 295: 296: void my_conrec_record( 297: my_conrec *cr_ptr 298: , apr_byte_t direction_flag 299: , my_tcp_pkt *d 300: ) { 301: int i=0; 302: 303: if (! cr_ptr->fp) { 304: return; 305: } 306: apr_file_printf(cr_ptr->fp, "%c,seq_no=%u,ack_no=%u,[", 307: (direction_flag)?'C':'S', d->tcp_seq_no, d->tcp_ack_no 308: ); 309: if (d->tcp_urg_bit) { 310: apr_file_printf(cr_ptr->fp, "urg:"); 311: } 312: if (0) if (d->tcp_ack_bit) { /* ACK ビットは必ず 1 になる */ 313: apr_file_printf(cr_ptr->fp, "ack:"); 314: } 315: if (d->tcp_psh_bit) { 316: apr_file_printf(cr_ptr->fp, "psh:"); 317: } 318: if (d->tcp_rst_bit) { 319: apr_file_printf(cr_ptr->fp, "rst:"); 320: } 321: if (d->tcp_syn_bit) { 322: apr_file_printf(cr_ptr->fp, "syn:"); 323: } 324: if (d->tcp_fin_bit) { 325: apr_file_printf(cr_ptr->fp, "fin:"); 326: } 327: apr_file_printf(cr_ptr->fp, "],size=%u", d->tcp_payload_size); 328: apr_file_printf(cr_ptr->fp, "\n"); 329: 330: for (i=0; i<d->tcp_payload_size; i++) { 331: apr_file_printf(cr_ptr->fp, "%02X", d->tcp_payload_pos[i]); 332: if (i && i%32 == 31) 333: apr_file_printf(cr_ptr->fp, "\n"); 334: } 335: apr_file_printf(cr_ptr->fp, "\n"); 336: } 337: 338: /* パケット(ディレクションとコードビット)による状態遷移 */ 339: 340: void my_tcp_trans (apr_byte_t direction_flag, my_tcp_pkt *d) { 341: 342: my_conrec *cr_ptr = NULL; 343: 344: char *client_ip = NULL; 345: apr_uint16_t client_port = 0; 346: char *server_ip = NULL; 347: apr_uint16_t server_port = 0; 348: 349: if (direction_flag) { 350: client_ip = d->ip_src_addr; 351: client_port = d->tcp_src_port; 352: server_ip = d->ip_dst_addr; 353: server_port = d->tcp_dst_port; 354: } else { 355: client_ip = d->ip_dst_addr; 356: client_port = d->tcp_dst_port; 357: server_ip = d->ip_src_addr; 358: server_port = d->tcp_src_port; 359: } 360: 361: if (direction_flag && d->tcp_syn_bit && !d->tcp_ack_bit) { 362: cr_ptr = my_conrec_new(client_ip, client_port, server_ip, server_port); 363: if (!cr_ptr) { 364: if(D2)puts("what's up ?! (1)"); 365: if(D2)printf("active=%d\n", count_active_conrec_data); 366: return; 367: } 368: cr_ptr->stat = SYN1; 369: cr_ptr->c_seq_no = d->tcp_seq_no; 370: return; 371: } 372: 373: cr_ptr = my_conrec_get(client_ip, client_port, server_ip, server_port); 374: if (!cr_ptr) { 375: return; 376: } 377: 378: 379: if (cr_ptr->stat == SYN1) { 380: if (!direction_flag && d->tcp_syn_bit) { 381: cr_ptr->stat = SYN2; 382: cr_ptr->s_seq_no = d->tcp_seq_no; 383: } 384: return; 385: } 386: 387: if (cr_ptr->stat == SYN2) { 388: if (direction_flag) { 389: cr_ptr->stat = ESTAB; 390: cr_ptr->c_seq_no = d->tcp_seq_no; 391: my_conrec_open(cr_ptr); 392: 393: if(D2)printf("[%s],%c,", cr_ptr->name, direction_flag?'C':'S'); 394: if(D2)printf("OPENED. active=%d\n", count_active_conrec_data); 395: return; 396: } 397: } 398: 399: if (d->tcp_payload_size != 0) { 400: my_conrec_record(cr_ptr, direction_flag, d); 401: } 402: 403: if (cr_ptr->stat == ESTAB) { 404: 405: if (direction_flag && d->tcp_fin_bit) { 406: cr_ptr->stat = C_FIN1; 407: if(0)printf("[%s],%c,", cr_ptr->name, direction_flag?'C':'S'); 408: if(0)printf("C_FIN1.\n"); 409: return; 410: } 411: 412: if (!direction_flag && d->tcp_fin_bit) { 413: cr_ptr->stat = S_FIN1; 414: if(0)printf("[%s],%c,", cr_ptr->name, direction_flag?'C':'S'); 415: if(0)printf("S_FIN1.\n"); 416: return; 417: } 418: 419: } 420: 421: if (cr_ptr->stat == C_FIN1) { 422: if (!direction_flag) { 423: if(D2)printf("[%s],%c,", cr_ptr->name, direction_flag?'C':'S'); 424: my_conrec_close(cr_ptr); 425: if(D2)printf("C_FIN1-ACK-CLOSED. active=%d\n", count_active_conrec_data); 426: return; 427: } 428: } 429: 430: if (cr_ptr->stat == S_FIN1) { 431: if (direction_flag) { 432: if(D2)printf("[%s],%c,", cr_ptr->name, direction_flag?'C':'S'); 433: my_conrec_close(cr_ptr); 434: if(D2)printf("S_FIN1-ACK-CLOSED. active=%d\n", count_active_conrec_data); 435: return; 436: } 437: } 438: 439: } 440: 441: /* 442: * 指定されたホストネームのIPアドレスの文字列を返す関数 443: * hostname に "" を指定した場合、動作するホストのIPアドレスが返る。 444: */ 445: 446: char * get_host_addr(const char * hostname, apr_pool_t *pool) { 447: apr_status_t rv = APR_SUCCESS; 448: apr_sockaddr_t *sa = NULL; 449: char *ipaddr = NULL; 450: rv = apr_sockaddr_info_get (&sa, hostname, APR_UNSPEC, 0, 0, pool); 451: if (rv != APR_SUCCESS) { 452: return NULL; 453: } 454: 455: rv = apr_sockaddr_ip_get (&ipaddr, sa); 456: if (rv != APR_SUCCESS) { 457: return NULL; 458: } 459: 460: return ipaddr; 461: } 462: 463: void print_usage (apr_file_t *out) { 464: apr_getopt_option_t *ptr = (apr_getopt_option_t *)opt_option; 465: 466: apr_file_printf(out, "Usage: %s ", PROG_NAME); 467: while (ptr && ptr->name) { 468: if (ptr->has_arg) { 469: apr_file_printf(out, "[ -%c <arg> | --%s <arg> ] ", ptr->optch, 470: ptr->name); 471: } else { 472: apr_file_printf(out, "[ -%c | --%s ] ", ptr->optch, ptr->name); 473: } 474: ptr++; 475: } 476: apr_file_printf(out, "\n"); 477: ptr = (apr_getopt_option_t *)opt_option; 478: while (ptr && ptr->name) { 479: apr_file_printf(out, "\t-%c, --%s\t: %s\n", ptr->optch, ptr->name, 480: ptr->description); 481: ptr++; 482: } 483: 484: } 485: 486: /* 487: * コマンドライン引数の処理 488: */ 489: 490: int my_pcap_init ( 491: int ac 492: , char **av 493: , my_pcap_params *params 494: , apr_file_t *astderr 495: , apr_pool_t *pool 496: ) { 497: apr_status_t rv = APR_SUCCESS; 498: 499: apr_getopt_t *opt = NULL; 500: int opt_ch = 0; 501: const char *opt_arg = NULL; 502: 503: const char * interface_str = NULL; 504: 505: if (ac < 2) { 506: print_usage(astderr); 507: return 0; 508: } 509: 510: rv = apr_getopt_init(&opt, pool, ac, (const char * const *)av); 511: 512: if (rv != APR_SUCCESS) { 513: apr_file_printf(astderr, "ERROR: apr_getopt_init\n"); 514: return 0; 515: } 516: 517: while ((rv = apr_getopt_long(opt, opt_option, &opt_ch, &opt_arg)) 518: == APR_SUCCESS) { 519: 520: switch (opt_ch) { 521: case 'p': 522: target_port = atoi(opt_arg); 523: break; 524: 525: case 'i': 526: interface_str = opt_arg; 527: break; 528: 529: case 'l': 530: log_dir = opt_arg; 531: break; 532: 533: #if defined(WIN32) || defined(WINDOWS) || defined(MSVC) 534: 535: case 'D': 536: { 537: char errbuf[80]; 538: if (! disp_devices(astderr, errbuf, sizeof(errbuf))) { 539: apr_file_printf(astderr, "ERROR: disp_devices: %s\n", errbuf); 540: } 541: } 542: return 0; 543: break; 544: 545: #endif 546: 547: case 'h': 548: print_usage(astderr); 549: return 0; 550: } 551: 552: } 553: 554: if (rv != APR_EOF || target_port<=0 || !interface_str || !log_dir) { 555: print_usage(astderr); 556: return 0; 557: } 558: 559: params->device = interface_str; 560: params->filter = apr_psprintf(pool, "tcp port %d", target_port); 561: 562: watcher_str = get_host_addr("", pool); 563: memset(conrec_tab, 0, sizeof(conrec_tab)); 564: return 1; 565: } 566: 567: void my_pcap_main( 568: apr_pool_t *pool 569: , apr_file_t *out 570: , my_tcp_pkt *d 571: ) { 572: 573: apr_byte_t direction_flag = 0; 574: 575: if (!d || !pool || !out) { 576: return; 577: } 578: 579: if (d->tcp_dst_port == target_port) { 580: direction_flag = 1; 581: } 582: 583: my_tcp_trans(direction_flag, d); 584: } 585: 586: /* 587: * 終期化処理 588: * コネクションテーブルの強制クローズ 589: */ 590: 591: void my_pcap_finally ( 592: apr_file_t *out 593: ) { 594: 595: int i=0; 596: my_conrec *cr_ptr = NULL; 597: for (i=0; i<CONREC_TAB_SIZE; i++) { 598: cr_ptr = & conrec_tab[i]; 599: if (cr_ptr->stat) { 600: if (cr_ptr->fp) { 601: apr_status_t rv = APR_SUCCESS; 602: char kbuf[32]; 603: 604: apr_file_printf(cr_ptr->fp, "0\r\n"); 605: 606: rv = apr_rfc822_date(kbuf, apr_time_now()); 607: if (rv == APR_SUCCESS) { 608: apr_file_printf(cr_ptr->fp, "Aborted-Date: %s\r\n", kbuf); 609: } 610: apr_file_printf(cr_ptr->fp, "\r\n"); 611: apr_file_close(cr_ptr->fp); 612: cr_ptr->fp = NULL; 613: } 614: if (cr_ptr->pool) { 615: apr_pool_destroy(cr_ptr->pool); 616: cr_ptr->pool = NULL; 617: } 618: } 619: } 620: apr_file_printf(out, "done.\n"); 621: }
KAKU PROJECT (2009) |