/* * mystab_pcap.c * libpcap を用いた TCP パケット解析用スタブ * pcap_loop で無限ループする。終了には * Ctrl-C 入力か kill コマンドで SIGINT シグナルを送る。 */ #define ETHER_HEADER_SIZE (6+6+2) #include "mystab_pcap.h" #include "apr_signal.h" #if defined(WIN32) || defined(WINDOWS) || defined(MSVC) /* * ネットワークデバイスのリストの出力ユーティリティ * (Windows のみ) * 返り値: * 成功:1 * 失敗:0 (errbuf にエラーメッセージが保存される) */ int disp_devices( apr_file_t *out , char *errbuf , int errbuf_size ) { pcap_if_t *alldevs = NULL; pcap_if_t *d = NULL; /* ネットワークデバイスのリストの取得 */ if (pcap_findalldevs(&alldevs, errbuf) == -1) { return 0; } if(!alldevs) { snprintf(errbuf, ERR_BUF_SIZE, "No interface\n"); return 0; } /* ネットワークデバイスのリストの表示 */ for(d=alldevs; d; d=d->next) { apr_file_printf(out, "%s\n", d->name); if (d->description) apr_file_printf(out, "\tdescription: %s\n\n", d->description); else apr_file_printf(out, "\t(No description)\n\n"); } pcap_freealldevs(alldevs); return 1; } #endif /* defined(WIN32) || defined(WINDOWS) || defined(MSVC) */ struct printer { pcap_handler f; int type; }; static void ether_flame_handler(u_char *, const struct pcap_pkthdr *, const u_char *); static struct printer printers[] = { { ether_flame_handler, DLT_EN10MB }, { NULL, 0 }, }; static char errbuf[ERR_BUF_SIZE]; static pcap_t *_my_pcap_init ( int ac , char **av , my_pcap_params *params , apr_file_t *astderr , apr_pool_t *pool ) { pcap_t *pd; struct bpf_program fcode; bpf_u_int32 localnet, netmask; if (my_pcap_init(ac, av, params, astderr, pool) == 0) { return NULL; } if(!params->device){ params->device = pcap_lookupdev(errbuf); if(! params->device){ apr_file_printf(astderr, "ERROR: pcap_lookupdev: %s\n", errbuf); return NULL; } } pd = pcap_open_live(params->device, params->snaplen, params->pflag, params->to_ms, errbuf); if(!pd){ apr_file_printf(astderr, "ERROR: pcap_open_live: %s\n", errbuf); return NULL; } else if(! params->filter) { /* no filter */ return pd; } else if(pcap_lookupnet(params->device, &localnet, &netmask, errbuf) < 0){ apr_file_printf(astderr, "ERROR: pcap_lookupnet: %s\n", errbuf); return NULL; } else if (pcap_compile(pd, &fcode, params->filter, params->Oflag, netmask) < 0) { apr_file_printf(astderr, "ERROR: pcap_compile(): %s\n", pcap_geterr(pd)); return NULL; } else if (pcap_setfilter(pd, &fcode) < 0) { apr_file_printf(astderr, "ERROR: pcap_setfilter(): %s\n", pcap_geterr(pd)); return NULL; } return pd; } static pcap_handler lookup_printer( int type ) { struct printer *p; for (p = printers; p->f; ++p) if (type == p->type) return p->f; /* not found */ return NULL; } typedef struct _ether_flame_hadler_data { apr_pool_t *pool; apr_file_t *out; my_tcp_pkt *d; } ether_flame_hadler_data; static void ether_flame_handler ( u_char * userdata , const struct pcap_pkthdr *h , const u_char *p ) { apr_byte_t *c = NULL; ether_flame_hadler_data *efhd = (ether_flame_hadler_data *)userdata; my_tcp_pkt *d = efhd->d; if (h->caplen < ETHER_HEADER_SIZE + 40) /* パケットサイズが小さい… */ return; memset(d, 0, sizeof(my_tcp_pkt)); /* TCP の先頭 */ d->ip_header_pos = (unsigned char*)p + ETHER_HEADER_SIZE; /* IP ヘッダのバイト数の取得。 * IP パケット先頭(イーサヘッダ後)の最初のバイトの下位4ビットの値×4バイト */ c = d->ip_header_pos; d->ip_header_size = (*c & 0xF)*4; /* IP データグラムのバイト数の取得。 * IP ヘッダのバイト数も含まれる。 * IP パケット先頭から3バイト目と4バイト目 */ d->ip_datagram_size = *(c+2)*256 + *(c+3); /* 送り元IP アドレスの取得。 * IP パケット先頭から13〜16バイト目 */ c = d->ip_header_pos + 12; snprintf(d->ip_src_addr, 16, "%d.%d.%d.%d", *c, *(c+1), *(c+2), *(c+3)); /* あて先IP アドレスの取得。 * IP パケット先頭から17〜20バイト目 */ c += 4; snprintf(d->ip_dst_addr, 16, "%d.%d.%d.%d", *c, *(c+1), *(c+2), *(c+3)); /* TCP の先頭 */ d->tcp_header_pos = d->ip_header_pos + d->ip_header_size; /* 送り元ポート番号とあて先ポート番頭の取得。 * TCP パケット先頭の1〜2バイト目:送り元ポート番号 * TCP パケット先頭の3〜4バイト目:あて先ポート番号 */ c = d->tcp_header_pos; d->tcp_src_port = *c * 256 + *(c+1); d->tcp_dst_port = *(c+2) * 256 + *(c+3); /* TCP シーケンス番号の取得 */ c = d->tcp_header_pos + 4; d->tcp_seq_no = *c*256*256*256+*(c+1)*256*256+*(c+2)*256+*(c+3); c += 4; d->tcp_ack_no = *c*256*256*256+*(c+1)*256*256+*(c+2)*256+*(c+3); /* TCP ヘッダのバイト数の取得。 * TCP パケット先頭から13バイト目の上位4ビットの値×4バイト */ c = d->tcp_header_pos + 12; d->tcp_header_size = (*c >> 4) * 4; /* コードビットの取得 * TCP パケットの14バイト目の下位6ビット。 */ { unsigned char code_bits = 0; c = d->tcp_header_pos + 13; code_bits = *c & 0x3F; d->tcp_urg_bit = code_bits>>5; d->tcp_ack_bit = (code_bits>>4) & 1; d->tcp_psh_bit = (code_bits>>3) & 1; d->tcp_rst_bit = (code_bits>>2) & 1; d->tcp_syn_bit = (code_bits>>1) & 1; d->tcp_fin_bit = (code_bits) & 1; } /* TCP ペイロードサイズ */ d->tcp_payload_size = d->ip_datagram_size - d->ip_header_size - d->tcp_header_size; /* TCP ペイロードの位置 */ d->tcp_payload_pos = d->tcp_header_pos + d->tcp_header_size; my_pcap_main(efhd->pool, efhd->out, d); } static pcap_t *pd = NULL; /* シグナルハンドラ * pcap_loop を抜ける */ static void my_signal_handler (int x) { if(0)fprintf(stderr, "SIGNAL: %d\n", x); if (pd) { pcap_breakloop(pd); } } int apr_my_main ( int ac, char **av, apr_file_t * astdin, apr_file_t * astdout, apr_file_t * astderr, apr_pool_t * pool ) { my_pcap_params params; pcap_handler printer; ether_flame_hadler_data efhd; my_tcp_pkt d; int rv = 0; /* シグナル設定 */ apr_signal(SIGINT, my_signal_handler); /* libpcap パラメータのデフォルト値の設定 */ params.device = NULL; params.snaplen = 65535; params.pflag =0; params.to_ms = 1000; params.Oflag = 0; params.cnt = -1; params.filter = NULL; pd = _my_pcap_init(ac, av, ¶ms, astderr, pool); if(!pd) { return 1; } printer = lookup_printer(pcap_datalink(pd)); if (!printer) { apr_file_printf(astderr, "ERROR: unknown data link type\n"); return 1; } efhd.pool = pool; efhd.out = astdout; efhd.d = &d; rv = pcap_loop(pd, params.cnt, printer, (u_char*)&efhd); if (rv == (-2)){ apr_file_printf(astderr, "WARNING: escaped from pcap_loop.\n"); } else { apr_file_printf(astderr, "ERROR: pcap_loop: %s\n", pcap_geterr(pd)); } if (pd) { pcap_close(pd); pd = NULL; } my_pcap_finally(astdout); /* 正常終了 */ return 0; }