ndpi工作流程

來源:互聯網
上載者:User

這裡以ndpi的常式ndpiReader.c為例,講述一下ndpi從抓包到最終分析出具體協議的流程。簡單來講ndpi是從下層開始逐層向上對資料包進行分析的。
先上一發自己畫的流程圖
這張圖是我在最開始看ndpi源碼的時候做的流程圖,還不是非常的清楚和正確,就連函數的調用關係也只是按先後順序畫的,現在看起來真是有點low,不過也大致說明了一些問題,也就懶得修改了。我會在接下來的文章中說明。這裡如果大家想真正瞭解其工作流程的話,最好自己通過gdb工具進行調試,進入example檔案夾,運行gdb -tui ndpiReader命令,在test檔案夾下找到.pcap檔案或者自己上網抓個包都行,然後在gdb命令列中設定斷點,運行 r -i *.pcap即可進入偵錯模式~~可以百度一下gdb調試相關知識。

第一步是程式的初始化,調用setupDetection()函數,這裡所做的工作也比較多,打算新寫一篇文章來專門講述此函數作用。
接下來會開啟線程調用libpcap庫函數對通過電腦網卡的資料包進行抓取,或者讀取傳入的.pcap檔案(具體的 如何運行等簡單操作可以參考官方給出的文檔,在doc檔案夾下)
接下來對每一個資料包(這裡需要明確兩個概念,資料包(packet)和資料流(flow),一個資料流中可能會有很多個資料包,就像我們申請一個網頁請求,由於頁面資訊很大,所以會分成很多個資料包來傳輸,但這些資料包同屬於一個資料流),首先對其資料連結層和IP層進行拆包分析pcap_packet_callback()函數,判斷是否為基於IP協議等,並獲得其源目的IP、協議類型等。
在接下來調用packet_processing()函數,進行傳輸層分析。在進行傳輸層分析時調用了get_ndpi_flow()函數,該函數返回ndpi_flow這個結構體(這裡需要注意ndpi_flow和ndpi_flow_struct兩個結構體的區別)。在get_ndpi_flow()函數中擷取傳輸層的資訊如源目的連接埠等資訊。然後根據(源目的IP、源目的連接埠、協議類型(tcp\udp))這五個元素計算出idx。

idx = (vlan_id + lower_ip + upper_ip + iph->protocol + lower_port + upper_port) % NUM_ROOTS;  ret = ndpi_tfind(&flow, &ndpi_thread_info[thread_id].ndpi_flows_root[idx], node_cmp);

這裡就是我剛開始開源碼時困擾我的地方,開始一直不知道idx的作用,後來發現程式維護了一個數組,用來記錄所有的資料流,而idx是用來標識不同的資料流,根據前面解析出資料包的五元組計算idx,然後查詢 ndpi_flows_root[]這個數組在索引為idx位置是否已經有了記錄。一般,對於一個資料流而言,該流的第一個資料包查詢時ndpi_flows_root[idx]為空白,則建立一個新的ndpi_flow對象並儲存到該位置處;等抓到該資料流的後續資料包時,因為屬於同一個流(即idx相同),所以ndpi_flows_root[idx]不為空白,則直接返回已經有的ndpi_flow即可。至此,我們得到了ndpi_flow這個結構體,這也是get_ndpi_flow()這個函數的意義。

接下來函數會調用ndpi_detection_process_packet()這個函數進行應用程式層分析。這也是應用協議分析的主體函數。注意這個函數傳進的參數是ndpi_flow_struct(下面記為flow),函數首先會對flow->packet即對packet這個結構體進行初始化。因為對於同一個流flow而言,在該結構體中有些變數在第一個資料包時已經初始化了,這些變數可能在特定情況下才會發生改變,比如檢測出了協議等;而對每一個資料包,flow中必須要變的就是flow->packet中的資訊。接下來會調用ndpi_connection_tracking()函數,這個函數的主要作用是判斷這個包的‘位置’,熟悉tcp協議的人都知道,一個tcp經過三向交握建立串連bababababa….這裡自行腦補,主要要知道syn,ack,seq,ack_seq四個變數的作用和功能。這個函數在資料包重組等功能中會有很重要的作用。這裡貼出部分代碼

    if(tcph->syn != 0 && tcph->ack == 0 && flow->l4.tcp.seen_syn == 0 && flow->l4.tcp.seen_syn_ack == 0       && flow->l4.tcp.seen_ack == 0) {      flow->l4.tcp.seen_syn = 1;    }//第一次    if(tcph->syn != 0 && tcph->ack != 0 && flow->l4.tcp.seen_syn == 1 && flow->l4.tcp.seen_syn_ack == 0       && flow->l4.tcp.seen_ack == 0) {      flow->l4.tcp.seen_syn_ack = 1;    }//第二次    if(tcph->syn == 0 && tcph->ack == 1 && flow->l4.tcp.seen_syn == 1 && flow->l4.tcp.seen_syn_ack == 1       && flow->l4.tcp.seen_ack == 0) {      flow->l4.tcp.seen_ack = 1;    }//第三次    //上面三句是三向交握相應的判斷語句    if((flow->next_tcp_seq_nr[0] == 0 && flow->next_tcp_seq_nr[1] == 0)       || (proxy_enabled && (flow->next_tcp_seq_nr[0] == 0 || flow->next_tcp_seq_nr[1] == 0))) {      if(tcph->ack != 0) {      //packet_direction表示方向是從源IP到目的IP\從目的IP到源IP    flow->next_tcp_seq_nr[flow->packet.packet_direction] =      ntohl(tcph->seq) + (tcph->syn ? 1 : packet->payload_packet_len);    if(!proxy_enabled) {      flow->next_tcp_seq_nr[1 -flow->packet.packet_direction] = ntohl(tcph->ack_seq);    }      }    } else if(packet->payload_packet_len > 0) {      /* check tcp sequence counters */      if(((u_int32_t)      (ntohl(tcph->seq) -       flow->next_tcp_seq_nr[packet->packet_direction])) >     ndpi_struct->tcp_max_retransmission_window_size) {    packet->tcp_retransmission = 1;    }

接下來會對ndpi_selection_packet進行設定,這個變數主要記錄每個資料包的下四層資訊。該變數是NDPI_SELECTION_BITMASK_PROTOCOL_SIZE類型,大概就是一個10101011這樣的東西,如下面代碼所示,把這幾個變數進行與或操作得到一個值,比如110111101中的兩個0就表示不是IPV6和不是TCP。

#define NDPI_SELECTION_BITMASK_PROTOCOL_SIZE            u_int32_t#define NDPI_SELECTION_BITMASK_PROTOCOL_IP          (1<<0)#define NDPI_SELECTION_BITMASK_PROTOCOL_INT_TCP         (1<<1)#define NDPI_SELECTION_BITMASK_PROTOCOL_INT_UDP         (1<<2)//移位操作#define NDPI_SELECTION_BITMASK_PROTOCOL_INT_TCP_OR_UDP      (1<<3)#define NDPI_SELECTION_BITMASK_PROTOCOL_HAS_PAYLOAD     (1<<4)#define NDPI_SELECTION_BITMASK_PROTOCOL_NO_TCP_RETRANSMISSION   (1<<5)#define NDPI_SELECTION_BITMASK_PROTOCOL_IPV6            (1<<6)#define NDPI_SELECTION_BITMASK_PROTOCOL_IPV4_OR_IPV6        (1<<7)#define NDPI_SELECTION_BITMASK_PROTOCOL_COMPLETE_TRAFFIC    (1<<8)

在接下來調用下面代碼,這裡的guessed_protocol_id 我還沒有搞明白是做什麼用的,之後用到再看吧

flow->guessed_protocol_id = (int16_t)ndpi_guess_protocol_id(ndpi_struct, protocol,                                sport, dport);    flow->protocol_id_already_guessed = 1;

最後,調用check_ndpi_flow_func()函數進行具體應用協議的檢測,這裡會根據tcp\udp\二者都不進入不同的介面。這裡的東西也比較多,暫時想著針對http協議類型再寫一篇文章,所以就不詳細敘述了。經過這個函數之後如果仍然沒有檢測出協議類型,那麼就繼續檢測下一個資料包直到檢測出該資料流的協議類型為止。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.