天天看點

netstat工具源碼剖析——網絡診斷必備

netstat是監控TCP/IP網絡的工具,可以顯示路由表、實際的網絡連接配接以及每一個網絡接口裝置的狀态資訊。用于顯示與IP、TCP、UDP和ICMP協定相關的統計資料,一般用于檢驗本機各端口的網絡連接配接情況。

網上關于netstat工具使用的文章多如牛毛,但是針對工具剖析的卻沒有,一篇難找。古人說:工欲善其事必先利其器。那麼我們一起來分析吧。

netstat的源碼位于net-tools工具包中,這是linux網絡的基本工具包,此外還包括arp,hostname,route等指令。

  項目連結:http://net-tools.sourceforge.net/

            下載下傳位址:https://sourceforge.net/projects/net-tools/files/latest/download

下載下傳後解壓,可以直接進行編譯了。

make netstat;可以進行netstat編譯。

make ifconfig;可以編譯ifconfig。

編譯過程中可以選擇部分選項,例如需要使用-M參數,那麼在config.h檔案中将#define HAVE_FW_MASQUERADE 0,如果要編譯就設定為1就好了。

編譯時候出錯需要進行如下修改:

1、在lib/inet_src.c中,108行加入break;

    107     default:

108        break;

2、在<b>lib/x25_sr.c   </b>

80 memcpy(&amp;rt.address, &amp;sx25.sx25_addr, sizeof(x25_address));

為:

memcpy(&amp;rt.address, &amp;sx25.sx25_addr, sizeof(sx25.sx25_addr));

     我們檢視makefile檔案如下:

<b>NET_LIB </b>= <b>$(NET_LIB_PATH)</b>/lib<b>$(NET_LIB_NAME)</b>.a

<b>NET_LIB_PATH </b>= lib

<b>NET_LIB_NAME </b>= net-tools

<b>CC     </b>

= gcc

<b>LDFLAGS </b>= <b>$(LOPTS)</b> -L<b>$(NET_LIB_PATH)</b>

<b>netstat:</b>        <b>$(NET_LIB)</b> netstat.o statistics.o

                <b>$(CC)</b> <b>$(LDFLAGS)</b> -o netstat netstat.o

statistics.o <b>$(NLIB)</b> <b>$(RESLIB)</b>

     其主要兩個程式檔案是statistics.c和netstat.c。

     當然還有一些相關的函數都是位于lib檔案夾中的。

     相關的系統檔案目錄在<b>lib/pathnames.h</b><b>檔案中</b><b></b>

<b>     </b><b>列印僞連結資訊的</b>實作函數位于<b>lib/masq_info.c</b><b>中,有</b>print_masq函數、read_masqinfo函數、ip_masq_info函數三個函數。

statistics.c檔案中定義了統計資訊相關的資料結構和函數,提供為netstat.c檔案使用。

enum State {

    number = 0, opt_number,

i_forward, i_inp_icmp, i_outp_icmp, i_rto_alg,

    MaxState

};

struct entry {

    char *title;

    char *out;

    enum State type;

struct statedesc {

    int indent;

struct entry Iptab[]

struct entry Icmptab[]

struct entry Tcptab[]

struct entry Udptab[]

struct entry

Tcpexttab[]

tabtab結構體,合入了entry。

struct tabtab {

    struct entry *tab;

    size_t size;

    int *flag;

struct tabtab snmptabs[]為tabtab結構體的數組。

struct tabtab snmptabs[] =

{

    {"Ip", Iptab, sizeof(Iptab),

&amp;f_raw},

    {"Icmp", Icmptab, sizeof(Icmptab),

    {"Tcp", Tcptab, sizeof(Tcptab),

&amp;f_tcp},

    {"Udp", Udptab, sizeof(Udptab),

&amp;f_udp},

    {"TcpExt", Tcpexttab, sizeof(Tcpexttab),

    {NULL}

netstat工具源碼剖析——網絡診斷必備

inittab函數通過qsort函數将Iptab,Icmptab,Tcptab,Udptab,Tcpexttab進行排序.

Parsesnmp函數入參為三個整數,主要用于打開/proc/net/snmp檔案和/proc/net/netstat,将句柄交給process_fd函數進行處理。通過/proc/net/snmp檔案可以得到各層網絡協定的收發包的情況。

     Printval函數将值進行列印.

位于netstat.c檔案的main函數中,

     先定義了一個選擇結構體數組,結構體由系統提供:

struct option

  const char *name;

  /* has_arg can't be an enum because some compilers complain about

     type mismatches in all the code

that assumes it is an int.  */

  int has_arg;

  int *flag;

  int val;

然後通過getopt_long函數來擷取指令行參數。設定相關變量,例如flag_tcp++,flag_udp++等等。

判斷變量設定合理性,有些參數不能同時設定,例如flag_int 、 flag_rou、flag_mas、flag_sta分别是接口表、路由表、僞ip、靜态統計資料等。

    if (flag_int + flag_rou + flag_mas + flag_sta

&gt; 1)

        usage();

     然後根據這些相關變量,執行對應的函數。

     例如:

    if (flag_int) {

        for (;;) {

            i = iface_info();

            if (!flag_cnt || i)

                break;

            sleep(1);

        }   

        return (i);

    }  

     每個變量判斷其是否被設定,最後會去系統的相應路徑檔案中擷取資訊。

#define _PATH_PROCNET_IGMP             

"/proc/net/igmp"

#define _PATH_PROCNET_IGMP6            

"/proc/net/igmp6"

#define _PATH_PROCNET_TCP              

"/proc/net/tcp"

#define _PATH_PROCNET_TCP6             

"/proc/net/tcp6"

#define _PATH_PROCNET_UDP              

"/proc/net/udp"

#define _PATH_PROCNET_UDP6             

"/proc/net/udp6"

#define _PATH_PROCNET_RAW              

"/proc/net/raw"

#define _PATH_PROCNET_RAW6             

"/proc/net/raw6"

#define _PATH_PROCNET_UNIX             

"/proc/net/unix"

#define _PATH_PROCNET_ROUTE            

"/proc/net/route"

#define _PATH_PROCNET_ROUTE6           

"/proc/net/ipv6_route"

#define _PATH_PROCNET_RTCACHE          

"/proc/net/rt_cache"

#define _PATH_PROCNET_AX25_ROUTE       

"/proc/net/ax25_route"

#define _PATH_PROCNET_NR                "/proc/net/nr"

#define _PATH_PROCNET_NR_NEIGH         

"/proc/net/nr_neigh"

#define _PATH_PROCNET_NR_NODES         

"/proc/net/nr_nodes"

#define _PATH_PROCNET_ARP              

"/proc/net/arp"

#define _PATH_PROCNET_AX25             

"/proc/net/ax25"

#define _PATH_PROCNET_IPX              

"/proc/net/ipx"

#define _PATH_PROCNET_IPX_ROUTE        

"/proc/net/ipx_route"

#define _PATH_PROCNET_ATALK            

"/proc/net/appletalk"

#define _PATH_PROCNET_IP_BLK           

"/proc/net/ip_block"

#define _PATH_PROCNET_IP_FWD           

"/proc/net/ip_forward"

#define _PATH_PROCNET_IP_ACC           

"/proc/net/ip_acct"

#define _PATH_PROCNET_IP_MASQ          

"/proc/net/ip_masquerade"

#define _PATH_PROCNET_NDISC            

"/proc/net/ndisc"

#define _PATH_PROCNET_IFINET6          

"/proc/net/if_inet6"

#define _PATH_PROCNET_DEV              

"/proc/net/dev"

#define _PATH_PROCNET_RARP             

"/proc/net/rarp"

#define _PATH_ETHERS                   

"/etc/ethers"

#define _PATH_PROCNET_ROSE_ROUTE       

"/proc/net/rose_routes"

#define _PATH_PROCNET_X25             

"/proc/net/x25"

#define _PATH_PROCNET_X25_ROUTE                "/proc/net/x25_routes"

#define _PATH_PROCNET_DEV_MCAST        

"/proc/net/dev_mcast"

/* pathname for the netlink device */

#define _PATH_DEV_ROUTE "/dev/route"

以netstat中-t參數為例,在main函數中看到是tcp_info()函數,我們調試下這個函數,為其添加列印資訊:

static int tcp_info(void)

    printf("xxxdebug:

tcp_info\n");

    INFO_GUTS6(_PATH_PROCNET_TCP,

_PATH_PROCNET_TCP6, "AF INET (tcp)",

               tcp_do_one);

}

其打開的路徑為_PATH_PROCNET_TCP或_PATH_PROCNET_TCP6,其中tcp_do_one為回調函數。

#define _PATH_PROCNET_TCP               "/proc/net/tcp"

函數INFO_GUTS6定義如下:

#define INFO_GUTS6(file,file6,name,proc)                \

 char buffer[8192];                                     \

 int rc = 0;                                            \

 int lnr = 0;                                           \

 if (!flag_arg || flag_inet) {                          \

    INFO_GUTS1(file,name,proc)                          \

 }                                                     

\

 if (!flag_arg || flag_inet6) {                         \

    INFO_GUTS2(file6,proc)                              \

 INFO_GUTS3

根據變量情況,調用INFO_GUTS1或者INFO_GUTS2。

 最後調用tcp_do_one函數。

在 tcp_do_one函數最後加入一行列印:

printf("xxxdebug: tcp_do_one\n");

編譯後運作:#netstat -t

# ./netstat -t

Active Internet connections (w/o servers)

Proto Recv-Q Send-Q Local Address          

Foreign Address         State     

xxxdebug: tcp_info

xxxdebug: tcp_do_one

tcp      401      0 xxxxxxxxxx    CLOSE_WAIT 

tcp        0      0 xxxxxxxxxxx     ESTABLISHED

tcp        0      0 xxxxxxxxxxxx    ESTABLISHED

tcp        0      0 xxxxxxxxxxxx      ESTABLISHED

tcp      361      0 xxxxxxxxxxxx      CLOSE_WAIT 

這裡看到tcp_do_one執行了8次,因為在/proc/net/tcp檔案有8行,

# cat /proc/net/tcp

  sl 

local_address rem_address   st

tx_queue rx_queue tr tm-&gt;when retrnsmt  

uid  timeout inode                                                    

   0: 0100007F:0CEA 00000000:0000 0A

00000000:00000000 00:00000000 00000000  

110        0 32096415 1

ffff880078c04000 100 0 0 10 0                 

   1: 00000000:006F 00000000:0000 0A

00000000:00000000 00:00000000 00000000    

0        0 47126999 1

ffff880078ef0780 100 0 0 10 0                 

   2: 00000000:0016 00000000:0000 0A

0        0 35410755 1

ffff88000477f480 100 0 0 10 0                 

   3: 98CD13AC:E05A CD8CCD8C:0050 08

00000000:00000191 00:00000000 00000000    

0        0 63139333 1

ffff8800369ac780 20 4 28 10 -1                 

   4: 98CD13AC:0016 FF47643A:C8FA 01

00000000:00000000 02:0007E41B 00000000    

0        0 63635147 3

ffff88007c1e4f00 22 4 29 10 7                 

   5: 98CD13AC:0016 FF47643A:C6B8 01

00000000:00000000 02:0006FDB5 00000000    

0        0 63634584 2

ffff88007c1e4000 21 4 27 6 4                  

   6: 98CD13AC:C88E 0D440B6A:0050 01

0        0 63158811 1

ffff8800369af480 20 4 31 10 18                

   7: 98CD13AC:DF24

0D440B6A:0050 08 00000000:00000169 00:00000000 00000000     0       

0 17490053 1 ffff880005547480 20 4 28 10 13 

而在INFO_GUTS1函數中會根據/proc/net/tcp内容進行循環:

#define INFO_GUTS1(file,name,proc)                      \

  procinfo = fopen((file), "r");                        \

  if (procinfo == NULL) {                               \

    if (errno != ENOENT) {        

                     \

      perror((file));                                   \

      return -1;                                        \

    }                                                  

    if (flag_arg || flag_ver)                           \

      ESYSNOT("netstat", (name));                       \

    if (flag_arg)                                       \

      rc = 1;                                           \

  } else {                                              \

    do {                                                \

      if (fgets(buffer, sizeof(buffer), procinfo))      \

        (proc)(lnr++, buffer);                          \

    } while (!feof(procinfo));                          \

    fclose(procinfo);                                   \

  }

            好了,後面大家有對某個參數感興趣的也可以同理的去檢視源碼,看看執行路徑。

            netstat主要是對/proc/net/下面的檔案進行解析顯示,讓人可以更加直覺的看到目前網絡的使用情況。

最後将netstat常用的參數附上。具體詳細的使用下一章會繼續。

-a或--all:顯示所有連線中的Socket;

-A&lt;網絡類型&gt;或--&lt;網絡類型&gt;:列出該網絡類型連線中的相關位址;

-c或--continuous:持續列出網絡狀态;

-C或--cache:顯示路由器配置的快取資訊;

-e或--extend:顯示網絡其他相關資訊;

-F或--fib:顯示FIB;

-g或--groups:顯示多重廣播功能群組組員名單;

-h或--help:線上幫助;

-i或--interfaces:顯示網絡界面資訊表單;

-l或--listening:顯示監控中的伺服器的Socket;

-M或--masquerade:顯示僞裝的網絡連線;

-N或--netlink或--symbolic:顯示網絡硬體外圍裝置的符号連接配接名稱;

-o或--timers:顯示計時器;

-p或--programs:顯示正在使用Socket的程式識别碼和程式名稱;

-r或--route:顯示Routing

Table;

-s或--statistice:顯示網絡工作資訊統計表;

-t或--tcp:顯示TCP傳輸協定的連線狀況;

-u或--udp:顯示UDP傳輸協定的連線狀況;

-v或--verbose:顯示指令執行過程;

-V或--version:顯示版本資訊;

-w或--raw:顯示RAW傳輸協定的連線狀況;

-x或--unix:此參數的效果和指定"-A

unix"參數相同;

--ip或--inet:此參數的效果和指定"-A inet"參數相同。

繼續閱讀