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(&rt.address, &sx25.sx25_addr, sizeof(x25_address));
為:
memcpy(&rt.address, &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),
&f_raw},
{"Icmp", Icmptab, sizeof(Icmptab),
{"Tcp", Tcptab, sizeof(Tcptab),
&f_tcp},
{"Udp", Udptab, sizeof(Udptab),
&f_udp},
{"TcpExt", Tcpexttab, sizeof(Tcpexttab),
{NULL}

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
> 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->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<網絡類型>或--<網絡類型>:列出該網絡類型連線中的相關位址;
-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"參數相同。