?

TCP/IP 網絡中故障節點的診斷方法

2008-07-14 10:05于長虹張偉鋒
電腦知識與技術 2008年18期

于長虹 張偉鋒

摘要:本文描述了一種在TCP/IP網絡中進行故障節點診斷的程序實現,該方法基于VxWorks操作系統的網絡測試儀環境,但此程序算法的實現,并不依賴于底層的操作系統及硬件環境,經過少量修改可以在任何提供TCP/IP協議棧的操作系統中實現,比如Linux,Windows等。

關鍵詞:ICMP;TCP;UDP;路由追蹤

中圖分類號:TP393文獻標識碼:A文章編號:1009-3044(2008)18-2pppp-0c

1 背景

網路故障的一般表現是網速變慢或者無法訪問互聯網或內網服務器,在現場進行網絡故障診斷時,往往需要借助各種工具軟件如Sniffer、ping、traceroute等進行逐步排查,最后經過分析,選擇懷疑的網絡節點,然后在局端或現場對懷疑的網絡節點進行各種連通性、替代性測試,方法步驟繁雜,而且往往無法準確診斷。

經過分析,故障診斷的過程,可以使用專用的設備,并編寫相應的診斷程序,自動完成網絡故障節點的測試和判斷。

2 算法和設計

當測試節點到達目的網絡位置的鏈路存在問題時,一般可能是:物理鏈路斷開(線纜或節點設備故障);目的地址的相應端口沒有開放,或者中間鏈路經過的設備(交換機,路由器等)禁止了協議或端口;終端設備故障。故,處理流程首先是找到測試節點到達連接服務器節點的路徑,確定經過的網絡節點位置,然后對節點中的各個位置實施連通性測試,最后根據測試結果判斷故障節點位置和原因。

2.1 網絡路由的查詢

該部分的功能類似于Linux系統中提供的命令traceroute,不同的是,該部分功能進行路由診斷依賴的協議不僅僅是ICMP。

ICMP的原理是鏈路上的節點設備都要在轉發該 ICMP 回顯請求報文之前將報文頭部的 TTL 值減 1,當報文的 TTL 值減少到 0 時,節點設備向源發回 ICMP 超時信息。該診斷實用程序通過向目的地發送具有不同生存時間 (TTL) 的 ICMP報文,確定至目的地的路由。通過發送 TTL 為 1 的第一個回顯報文并且在隨后的發送中每次將 TTL 值加 1,直到目標響應或達到最大 TTL 值,可以確定鏈路經過的路由。通過檢查鏈路中間節點設備發回的 ICMP 超時信息,可以確定故障節點。

如果使用ICMP協議無法完成測試,則改為使用UDP協議和TCP協議分別進行路由偵測。源發出UDP數據包,源端口使用隨機的大于32768的高段端口號,目的端口從33434開始依此遞增,直至33434+29,同時TTL從1開始依此遞增,直至1+29=30。節點設備送回的 ICMP超時報文,使得源可以偵測到鏈路上每一個節點。

2.2 網絡節點診斷

向節點發送TCP握手信號,如果該節點可以通過connect連接成功,表示節點可以正常連接,如果回應RST,表示該節點禁止了該端口的訪問,如果該節點長時間不回復SYN,也可以認為該節點禁止端口。

因此,依據上述現象可以很容易判斷當前故障節點——離測試者最近的故障點,可以被認定為當前網絡故障點。

2.3 故障節點位置的判斷策略

如果路由尋找完整,一般能夠找到節點。在所有不回應SYN包或者回應RST包的

節點中,應該是離源最近跳數的節點設備將端口關閉。

如果路由尋找不完整,有可能找不到所找的故障點。如果在找到的n個節點中,只有非最遠離源的一個節點不回應,或回應RST包,則不能確定故障節點;如果是包括最遠離源在內的一個或多個節點不回應或回應RST包,則最右端節點可能為故障節點,但并不能確定在整個路由中的故障節點所在,因為路由不完整。

2.4 不能覆蓋的異常情況

如果使用ICMP和UDP都無法尋找到完整路由,則有可能找不到故障節點,但這種情況非常少,因為根據UDP的測試原理,除非中間節點將大于32768的端口全部封掉,否則都可以得到完整的路由路徑。

3 代碼片段和程序流程

3.1 整體框架代碼

int f_procon_scan_showerr(char *re_info)

{char err_node[16];

inet_ntoa_b(info_scan.node[info_scan.err_num],err_node);

if(err_tcpscan == ERR_PORTSCAN_ROUTE_HALF){

sprintf(re_info,"路由信息不完整,故障點可能是:%s",err_node);

}else if(err_tcpscan == ERR_PORTSCAN_ROUTE_NO){

sprintf(re_info,"未找到達到目的地址的路徑,無法診斷故障");

}else{

sprintf(re_info,"路由信息完整,故障點是:%s",err_node);

}

return OK;

}

static int quitflag=0;

int f_procon_scan_tcp(void)

{char * re_info; /* 測試完后返回的信息 */

int re_find;

int i,rv;

char buf[512];

int numBytes,count;

int on,len;

int ctrlSock;

struct sockaddr_in ctrlAddr;

struct router_node bak_router_node;

sprintf(buf,"正在使用ICMP獲取路由信息...");

server_virtual_display_output(buf,0);

bzero((char *)&info_scan,sizeof(info_scan));

re_find = find_node(0);/*使用ICMP協議*/

if(info_scan.number == 0){/* 沒有正確找到到目的地址的路由信息,嘗試使用udp協議查找*/

sprintf(buf,"正在使用UDP獲取路由信息...");

server_virtual_display_output(buf,0);

bzero((char *)&info_scan,sizeof(info_scan));

re_find = find_node(1);/*使用UDP協議*/

if(info_scan.number == 0){

err_tcpscan = ERR_PORTSCAN_ROUTE_NO;

return OK;

}

}else if(re_find == ERROR){/*獲取不完整路徑,嘗試用udp獲取*/

sprintf(buf,"正在使用UDP獲取路由信息...");

server_virtual_display_output(buf,0);

memcpy(&bak_router_node,&info_scan,sizeof(info_scan));

bzero((char *)&info_scan,sizeof(info_scan));

re_find = find_node(1);/*使用UDP協議*/

if(info_scan.number == 0){/*udp沒有獲取到路徑,則恢復icmp的路徑*/

memcpy(&info_scan,&bak_router_node,sizeof(bak_router_node));

re_find=ERROR;

}else if(re_find == ERROR){/*udp獲取的也是不完整路徑,則進行比較,選最多的*/

if(info_scan.number

memcpy(&info_scan,&bak_router_node,sizeof(bak_router_node));

}

}

}

if(re_find == ERROR){

err_tcpscan = ERR_PORTSCAN_ROUTE_HALF;

}else{

err_tcpscan = ERR_PORTSCAN_ROUTE_OK;

}

quitflag=1;

info_scan.node[info_scan.number].s_addr=self_ip;

for(i=info_scan.number-1;i>=0;i--){

/*創建socket*/

ctrlSock = socket (AF_INET, SOCK_STREAM, 0);

if (ctrlSock < 0){

sprintf(buf,"無法創建socket");

server_virtual_display_output(buf,0);

return (ERROR);

}

/*設置socket為非阻塞模式*/

on=TRUE;

if(ioctl(ctrlSock,FIONBIO,(int)&on)<0){

printf("set socket to no block is error ");

}

ctrlAddr.sin_family= AF_INET;

ctrlAddr.sin_addr.s_addr = info_scan.node[i].s_addr;

ctrlAddr.sin_port= htons(s_procon_info.port);

if(connect(ctrlSock,(struct sockaddr *)&ctrlAddr, sizeof (ctrlAddr))< 0){

if(!((errno==EINPROGRESS) || (errno==EALREADY))){

shutdown(ctrlSock,2);

close(ctrlSock);

continue;

}

}

rv=server_wait_for_write_timeout(ctrlSock,&quitflag);

if(rv!=0){

shutdown(ctrlSock,2);

close(ctrlSock);

break;

}

shutdown(ctrlSock,2);

close(ctrlSock);

}

if(i<0){/*沒有一個通的,或最近的也不通,則認為是最近的有問題*/

i=0;

}else if(i!=info_scan.number-1){/*不是最后一個不同,則認為就是他了*/

i++;

}else if(err_tcpscan == ERR_PORTSCAN_ROUTE_OK){/*最后一個竟然也是通的,則認為是服務器本身了*/

i++;

}

info_scan.err_num=i;

return OK;

}

3.2 查找路由節點函數

static int find_node(int flag)

{int dst_ip, gateway;

int i,num;

int result = OK;

S_TRACERT_INTERFACES info_tracert;

S_TRACERT_INTERS *intrs;

struct in_addr ip_tra;

dst_ip = s_procon_info.ip_remote.s_addr; /* tracert ip is test ip */

switch(s_netcon_info.mode){

case D_NETCON_MODE_STATIC_IP:

gateway = s_netcon_info.sta_ip.ip_router.s_addr;

self_ip = s_netcon_info.sta_ip.ip_local.s_addr;

break;

case D_NETCON_MODE_DHCPC:

gateway = s_netcon_info.dhcpc.ip_router[0].s_addr;

self_ip = s_netcon_info.dhcpc.ip_local.s_addr;

break;

case D_NETCON_MODE_PPPOEH:

gateway = s_netcon_info.pppoeh.ip_remote.s_addr;

self_ip = s_netcon_info.pppoeh.ip_local.s_addr;

break;

}

f_tracert_routine(dst_ip,gateway,flag); /* exec tracert for find node */

/* 如果tracert未結束,查看是否出現超時找不到路由情況,如果是,終止測試 */

/* 如果tracert停止,看是否追蹤到最終的路由 */

while(!v_tracert_end){

f_tracert_show((char *)&info_tracert); /* 取信息,判斷 */

for(i=0;i

intrs=&info_tracert.tracert_info[i];

ip_tra.s_addr = intrs->ip;

if(intrs->ip == 0){

info_tracert.number -= 1;

f_tracert_end();

result = ERROR;

}

}

taskDelay(sysClkRateGet()/2);

}

f_tracert_show((char *)&info_tracert);

info_scan.number = info_tracert.number;

/* save the node infomation to my struct */

for(i=0;i

intrs=&info_tracert.tracert_info[i];

info_scan.node[i].s_addr = intrs->ip;

}

num = info_scan.number - 1;

if(info_scan.node[num].s_addr == 0){

info_scan.number -= 1;

}

return result;

}

3.3 涉及到的數據結構

保存狀態的結構體。

static struct router_node{

int number; /* 到目的地址能找到的節點總數 如果為0,表示未找到路由*/

int err_num; /* 詢查所有節點,最后一個對端口無回應的節點序號*/

struct in_addr node[31]; /* 用Tracert查到的路由節點地址 */

char f_send[30]; /* 向相應節點成功發送TCP SYN包標志 ,成功置 1*/

charf_recv[30]; /* 成功接收各節點回復包標志,接收到置 1 */

int send_seq[30]; /* 發送的各SYN包的SEQ號,用來判斷接收包 */

unsigned char flag[30]; /* 如果接收返回包,保存返回包的TCP FLAG字段 */

}info_scan;

錯誤狀態如下:

#define ERR_PORTSCAN_ROUTE_NO 0x01

#define ERR_PORTSCAN_ROUTE_OK 0x02

#define ERR_PORTSCAN_ROUTE_HALF 0x03

#define ERR_PORTSCAN_SEND_SYN 0x11 //向網絡節點發送SYN同步包出錯

4 應用案例

現有一計算機終端,無法登錄其開通的網絡多媒體點播服務系統,但可以登錄其它網站,使用網絡測試儀的網絡故障診斷軟件來診斷該案例。

首先,通過用戶界面,填入多媒體點播系統的IP地址(如202.102.249.174)極其端口號(1026),然后點擊測試,診斷軟件首先查找從局域網絡到達202.102.249.174的路由如下:

1<1 ms<1 ms<1 ms192.168.15.1

2 *** Request timed out.

3 2 ms 1 ms 1 mshn.kd.ny.adsl [125.42.110.1]

4<1 ms<1 ms<1 mspc17.zz.ha.cn [61.168.254.17]

5<1 ms<1 ms<1 mspc58.zz.ha.cn [61.168.252.58]

6<1 ms<1 ms<1 ms202.102.249.174

然后,軟件將根據算法,從最后一個節點開始診斷,發現直到hn.kd.ny.adsl時,1026端口的連接測試不能通過,從而確定,問題是因為hn.kd.ny.adsl設備禁止了1026端口。向局端工程師確認,并修改多媒體登錄系統的端口為其它端口(8080),可以登錄,問題解決。

5 后記

使用該算法的網絡測試儀產品已經研制成功,該產品同時具備了ping、sniffer等更多的網絡功能,可以更好的替代網絡維護人員隨聲攜帶的筆記本電腦和其它設備,簡便地進行網絡故障的診斷。

參考文獻:

[1](美)科默(Comer,D.E.),林瑤,蔣慧,等,譯.用TCP/IP進行網際互聯(第1卷):原理、協議與結構.北京:電子工業出版社,2001,5.

[2](美)W.Richard Stevens,范建華,等,譯.TCP/IP詳解.北京:機械工業出版社,2000,4.

[3](美)DonnaL.Harrington,童小林,等,譯.CCNP實戰指南:故障排除.北京:人民郵電出版社,2003,12.

[4](美)史蒂文斯,(美)芬納,(美)魯道夫,楊繼張,譯. UNIX網絡編程.北京:清華大學出版社,2006,1.

收稿日期:2008-03-10

作者簡介:于長虹(1982-),男,網絡工程師,主要研究方向:網絡管理技術;張偉鋒,洛陽師范學院信息技術學院。

91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合