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) |