ktcpdump2.c

最終更新:2010/1/12

ktcpdump2.c

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: }
Copyright (C) KAKU PROJECT (2009)KAKU PROJECT (2009)