netstat工具源码剖析——网络诊断必备

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

1.1.1 编译

下载后解压,可以直接进行编译了。

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、在lib/x25_sr.c  

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

为:

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

     我们查看makefile文件如下:

NET_LIB = $(NET_LIB_PATH)/lib$(NET_LIB_NAME).a

NET_LIB_PATH = lib

NET_LIB_NAME = net-tools

CC      = gcc

LDFLAGS = $(LOPTS) -L$(NET_LIB_PATH)

netstat:        $(NET_LIB) netstat.o statistics.o

                $(CC) $(LDFLAGS) -o netstat netstat.o statistics.o $(NLIB) $(RESLIB)

     其主要两个程序文件是statistics.cnetstat.c

     当然还有一些相关的函数都是位于lib文件夹中的。

     相关的系统文件目录在lib/pathnames.h文件中

     打印伪链接信息的实现函数位于lib/masq_info.c中,有print_masq函数、read_masqinfo函数、ip_masq_info函数三个函数。

1.1.2 statistics数据结构和部分函数

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;

    char *title;

};

struct entry Iptab[]

struct entry Icmptab[]

struct entry Tcptab[]

struct entry Udptab[]

struct entry Tcpexttab[]

tabtab结构体,合入了entry

struct tabtab {

    char *title;

    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), &f_raw},

    {"Tcp", Tcptab, sizeof(Tcptab), &f_tcp},

    {"Udp", Udptab, sizeof(Udptab), &f_udp},

    {"TcpExt", Tcpexttab, sizeof(Tcpexttab), &f_tcp},

    {NULL}

};

 

netstat工具源码剖析——网络诊断必备_第1张图片

1.1.2.1  函数

inittab函数通过qsort函数将IptabIcmptab,Tcptab,Udptab,Tcpexttab进行排序.

Parsesnmp函数入参为三个整数,主要用于打开/proc/net/snmp文件和/proc/net/netstat,将句柄交给process_fd函数进行处理。通过/proc/net/snmp文件可以得到各层网络协议的收发包的情况。

     Printval函数将值进行打印.

1.1.3 main主程序逻辑

位于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_rouflag_masflag_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"

1.1.4 tcp链接显示

以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

xxxdebug: tcp_do_one

xxxdebug: tcp_do_one

tcp      401      0 xxxxxxxxxx    CLOSE_WAIT 

xxxdebug: tcp_do_one

tcp        0      0 xxxxxxxxxxx     ESTABLISHED

xxxdebug: tcp_do_one

tcp        0      0 xxxxxxxxxxxx    ESTABLISHED

xxxdebug: tcp_do_one

tcp        0      0 xxxxxxxxxxxx      ESTABLISHED

xxxdebug: tcp_do_one

tcp      361      0 xxxxxxxxxxxx      CLOSE_WAIT 

xxxdebug: tcp_do_one

这里看到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 00000000:00000000 00:00000000 00000000     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 00000000:00000000 00:00000000 00000000     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/下面的文件进行解析显示,让人可以更加直观的看到当前网络的使用情况。

1.1.5 参数选项

最后将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或--numeric:直接使用ip地址,而不通过域名服务器;

-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" 参数相同。

你可能感兴趣的:(netstat工具源码剖析——网络诊断必备)