tshark現在的數據包獲取方式有兩種,分別是讀文件、網口監聽(af-packet原始套接字)。兩種方式在包獲取上,都是通過讀文件的形式;存在文件io操作,在專門處理大流量的情境下, 我們復用wireshark去做功能開發是不適合的,在文件io這部分很消耗性能。
此前什么iptables和轉發nat需要配置
準備工作
../build/CMakeFiles/tshark.dir/link.txt 鏈接-lnetfilter_queue庫
通過iptables提供數據包,跳過文件讀寫的過程,可以大大提升性能(沒有進行性能測試)
一、將iptables加到收包方式中
在原始代碼中,real_main接口為tshark工具的入口函數,前半部分都是參數解析(我們不關注),在中間部分找到,收包代碼;
其中第一個條件if (cf_name)這里是讀文件的形式,使用是通過-r *.pcap判斷的,第二個else位置,里面是capture用來監聽網口通過-I eth0使用。
追加第三個iptables收包方式。
直接固定通過iptables收包。
cf_init_iptables接口完成初始化工作,cf是全局的數據,數據包信息,數據包狀態相關,比如處理多少,丟掉多少,提供的解碼工具等外來數據。里面還加了一個wtap空間,這里是關聯到每個數據包的,保存數據包內容。
cf_status_t cf_init_iptables(capture_file *cf,unsigned int type, gboolean is_tempfile, int *err) { ? wtap? *wth; ? gchar *err_info; ? wth = iptables_get_wtap_init(); ? ? /* The open succeeded.? Fill in the information for this file. */ ? /* Create new epan session for dissection. */ ? epan_free(cf->epan); ? cf->epan = tshark_epan_new(cf); ? cf->provider.wth = wth; ? cf->f_datalen = 0; /* not used, but set it anyway */ ? /* Set the file name because we need it to set the follow stream filter. ???? XXX - is that still true?? We need it for other reasons, though, ???? in any case. */ ? /* Indicate whether it's a permanent or temporary file. */ ? cf->is_tempfile = is_tempfile; ? /* No user changes yet. */ ? cf->unsaved_changes = FALSE; ? cf->cd_t????? = WTAP_FILE_TYPE_SUBTYPE_PCAP; ? cf->open_type = type; ? cf->count???? = 0; ? cf->drops_known = FALSE; ? cf->drops???? = 0; ? cf->snap????? = 262144; ? nstime_set_zero(&cf->elapsed_time); ? cf->provider.ref = NULL; ? cf->provider.prev_dis = NULL; ? cf->provider.prev_cap = NULL; ? cf->state = FILE_READ_IN_PROGRESS; ? wtap_set_cb_new_ipv4(cf->provider.wth, add_ipv4_name); ? wtap_set_cb_new_ipv6(cf->provider.wth, (wtap_new_ipv6_callback_t) add_ipv6_name); ? // 輸出格式初始化 ? write_preamble(cf); ? return CF_OK; } |
running_get_pkt_from_nfqueue0接口完成iptables初始化,創建接收隊列數據包的回調,將數據包接入到tshrak的解密解碼邏輯。
void running_get_pkt_from_nfqueue0(void) { struct nfq_handle *h; struct nfq_q_handle *qh; struct nfnl_handle *nh; int fd; int rv; char buf[4096] __attribute__ ((aligned)); printf("opening library handle\n"); h = nfq_open();//創建 netfilter_queue if (!h) {//創建失敗 fprintf(stderr, "error during nfq_open()\n"); exit(1); } printf("unbinding existing nf_queue handler for AF_INET (if any)\n");//解綁已經存在的隊列 if (nfq_unbind_pf(h, AF_INET) < 0) { fprintf(stderr, "error during nfq_unbind_pf()\n"); exit(1); } printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");//綁定上我們創建的隊列 if (nfq_bind_pf(h, AF_INET) < 0) { fprintf(stderr, "error during nfq_bind_pf()\n"); exit(1); } printf("binding this socket to queue '0'\n");//cb是回調函數 qh = nfq_create_queue(h,? 0, &cb, NULL); if (!qh) { fprintf(stderr, "error during nfq_create_queue()\n"); exit(1); } printf("setting copy_packet mode\n"); if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {//設置的包處理模式 fprintf(stderr, "can't set packet_copy mode\n"); exit(1); } fd = nfq_fd(h); for (;;) { if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) { //printf("pkt received\n"); nfq_handle_packet(h, buf, rv); continue; } /* if your application is too slow to digest the packets that ?* are sent from kernel-space, the socket buffer that we use ?* to enqueue packets may fill up returning ENOBUFS. Depending ?* on your application, this error may be ignored. Please, see ?* the doxygen documentation of this library on how to improve ?* this situation. ?*/ if (rv < 0 && errno == ENOBUFS) { printf("losing packets!\n"); continue; } perror("recv failed"); break; } printf("unbinding from queue 0\n"); nfq_destroy_queue(qh);//摧毀隊列,退出 #ifdef INSANE /* normally, applications SHOULD NOT issue this command, since ?* it detaches other programs/sockets from AF_INET, too ! */ printf("unbinding from AF_INET\n"); nfq_unbind_pf(h, AF_INET); #endif printf("closing library handle\n"); nfq_close(h); exit(0); } |
回調函數,這里需要做個額外的事情,因為iptables是一個三層工具,拿不到mac信息,所以這里需要加一個mac頭,位置看注釋。將數據包發給處理函數
int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, ????????? struct nfq_data *nfa, void *data) { //printf("entering callback\n"); struct nfqnl_msg_packet_hdr *ph; int id = 0; ph = nfq_get_msg_packet_hdr(nfa); if (ph) { ??? id = ntohl(ph->packet_id); } struct iphdr *ip_header; unsigned char *data_buf; int data_len = nfq_get_payload(nfa, &data_buf); if (data_len > 0) { ??? ip_header = (struct iphdr *)data_buf; ??? //printf("Source IP: %s\n", inet_ntoa(*(struct in_addr *)&ip_header->saddr)); ??? //printf("Destination IP: %s\n", inet_ntoa(*(struct in_addr *)&ip_header->daddr)); // 構造新的緩沖區 ??? int new_buf_len = 14 + data_len; ??? unsigned char *new_buf = (unsigned char *)malloc(new_buf_len); ??? if (new_buf == NULL) { ??????? perror("malloc"); ??????? return nfq_set_verdict(qh, id, NF_DROP, 0, NULL); ??? } ??? // 將MAC頭復制到新的緩沖區 ??? memcpy(new_buf, temp_mac_header, 14); ??? // 將data_buf復制到新的緩沖區中緊隨MAC頭的位置 ??? memcpy(new_buf + 14, data_buf, data_len); process_iptables_queue_packet_signle(new_buf_len,new_buf); ?? ?? free(new_buf); } ??? return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); } |
處理函數,初始化一個空間,數據包的所有內容保存在這里,用完釋放
void process_iptables_queue_packet_signle(int datalen, unsigned char *data) { capture_file *cf = &cfile; int create_proto_tree = 1; // 是否生成解析樹 int print_detile = 1;????? // 是否打印詳細信息 epan_dissect_t *edt = epan_dissect_new(cf->epan, create_proto_tree, print_detile); iptables_set_wth_rec_values(cf->provider.wth,datalen); process_packet_single_pass(cf,edt,0,wtap_get_rec(cf->provider.wth),data,0); if (edt) { ????? epan_dissect_free(edt); ????? edt = NULL; ??? } } |
這里對iptables的patch就完成了,試驗一下
Run/tshark執行,提示綁定隊列
重放數據包正常打印解析結果