于長虹 張偉鋒
摘要:本文描述了一種在TCP/IP網(wǎng)絡(luò)中進行故障節(jié)點診斷的程序?qū)崿F(xiàn),該方法基于VxWorks操作系統(tǒng)的網(wǎng)絡(luò)測試儀環(huán)境,但此程序算法的實現(xiàn),并不依賴于底層的操作系統(tǒng)及硬件環(huán)境,經(jīng)過少量修改可以在任何提供TCP/IP協(xié)議棧的操作系統(tǒng)中實現(xiàn),比如Linux,Windows等。
關(guān)鍵詞:ICMP;TCP;UDP;路由追蹤
中圖分類號:TP393文獻標識碼:A文章編號:1009-3044(2008)18-2pppp-0c
1 背景
網(wǎng)路故障的一般表現(xiàn)是網(wǎng)速變慢或者無法訪問互聯(lián)網(wǎng)或內(nèi)網(wǎng)服務(wù)器,在現(xiàn)場進行網(wǎng)絡(luò)故障診斷時,往往需要借助各種工具軟件如Sniffer、ping、traceroute等進行逐步排查,最后經(jīng)過分析,選擇懷疑的網(wǎng)絡(luò)節(jié)點,然后在局端或現(xiàn)場對懷疑的網(wǎng)絡(luò)節(jié)點進行各種連通性、替代性測試,方法步驟繁雜,而且往往無法準確診斷。
經(jīng)過分析,故障診斷的過程,可以使用專用的設(shè)備,并編寫相應(yīng)的診斷程序,自動完成網(wǎng)絡(luò)故障節(jié)點的測試和判斷。
2 算法和設(shè)計
當(dāng)測試節(jié)點到達目的網(wǎng)絡(luò)位置的鏈路存在問題時,一般可能是:物理鏈路斷開(線纜或節(jié)點設(shè)備故障);目的地址的相應(yīng)端口沒有開放,或者中間鏈路經(jīng)過的設(shè)備(交換機,路由器等)禁止了協(xié)議或端口;終端設(shè)備故障。故,處理流程首先是找到測試節(jié)點到達連接服務(wù)器節(jié)點的路徑,確定經(jīng)過的網(wǎng)絡(luò)節(jié)點位置,然后對節(jié)點中的各個位置實施連通性測試,最后根據(jù)測試結(jié)果判斷故障節(jié)點位置和原因。
2.1 網(wǎng)絡(luò)路由的查詢
該部分的功能類似于Linux系統(tǒng)中提供的命令traceroute,不同的是,該部分功能進行路由診斷依賴的協(xié)議不僅僅是ICMP。
ICMP的原理是鏈路上的節(jié)點設(shè)備都要在轉(zhuǎn)發(fā)該 ICMP 回顯請求報文之前將報文頭部的 TTL 值減 1,當(dāng)報文的 TTL 值減少到 0 時,節(jié)點設(shè)備向源發(fā)回 ICMP 超時信息。該診斷實用程序通過向目的地發(fā)送具有不同生存時間 (TTL) 的 ICMP報文,確定至目的地的路由。通過發(fā)送 TTL 為 1 的第一個回顯報文并且在隨后的發(fā)送中每次將 TTL 值加 1,直到目標響應(yīng)或達到最大 TTL 值,可以確定鏈路經(jīng)過的路由。通過檢查鏈路中間節(jié)點設(shè)備發(fā)回的 ICMP 超時信息,可以確定故障節(jié)點。
如果使用ICMP協(xié)議無法完成測試,則改為使用UDP協(xié)議和TCP協(xié)議分別進行路由偵測。源發(fā)出UDP數(shù)據(jù)包,源端口使用隨機的大于32768的高段端口號,目的端口從33434開始依此遞增,直至33434+29,同時TTL從1開始依此遞增,直至1+29=30。節(jié)點設(shè)備送回的 ICMP超時報文,使得源可以偵測到鏈路上每一個節(jié)點。
2.2 網(wǎng)絡(luò)節(jié)點診斷
向節(jié)點發(fā)送TCP握手信號,如果該節(jié)點可以通過connect連接成功,表示節(jié)點可以正常連接,如果回應(yīng)RST,表示該節(jié)點禁止了該端口的訪問,如果該節(jié)點長時間不回復(fù)SYN,也可以認為該節(jié)點禁止端口。
因此,依據(jù)上述現(xiàn)象可以很容易判斷當(dāng)前故障節(jié)點——離測試者最近的故障點,可以被認定為當(dāng)前網(wǎng)絡(luò)故障點。
2.3 故障節(jié)點位置的判斷策略
如果路由尋找完整,一般能夠找到節(jié)點。在所有不回應(yīng)SYN包或者回應(yīng)RST包的
節(jié)點中,應(yīng)該是離源最近跳數(shù)的節(jié)點設(shè)備將端口關(guān)閉。
如果路由尋找不完整,有可能找不到所找的故障點。如果在找到的n個節(jié)點中,只有非最遠離源的一個節(jié)點不回應(yīng),或回應(yīng)RST包,則不能確定故障節(jié)點;如果是包括最遠離源在內(nèi)的一個或多個節(jié)點不回應(yīng)或回應(yīng)RST包,則最右端節(jié)點可能為故障節(jié)點,但并不能確定在整個路由中的故障節(jié)點所在,因為路由不完整。
2.4 不能覆蓋的異常情況
如果使用ICMP和UDP都無法尋找到完整路由,則有可能找不到故障節(jié)點,但這種情況非常少,因為根據(jù)UDP的測試原理,除非中間節(jié)點將大于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協(xié)議*/
if(info_scan.number == 0){/* 沒有正確找到到目的地址的路由信息,嘗試使用udp協(xié)議查找*/
sprintf(buf,"正在使用UDP獲取路由信息...");
server_virtual_display_output(buf,0);
bzero((char *)&info_scan,sizeof(info_scan));
re_find = find_node(1);/*使用UDP協(xié)議*/
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協(xié)議*/
if(info_scan.number == 0){/*udp沒有獲取到路徑,則恢復(fù)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--){ /*創(chuàng)建socket*/ ctrlSock = socket (AF_INET, SOCK_STREAM, 0); if (ctrlSock < 0){ sprintf(buf,"無法創(chuàng)建socket"); server_virtual_display_output(buf,0); return (ERROR); } /*設(shè)置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){/*最后一個竟然也是通的,則認為是服務(wù)器本身了*/ i++; } info_scan.err_num=i; return OK; } 3.2 查找路由節(jié)點函數(shù) 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未結(jié)束,查看是否出現(xiàn)超時找不到路由情況,如果是,終止測試 */ /* 如果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 涉及到的數(shù)據(jù)結(jié)構(gòu)
保存狀態(tài)的結(jié)構(gòu)體。
static struct router_node{
int number; /* 到目的地址能找到的節(jié)點總數(shù) 如果為0,表示未找到路由*/
int err_num; /* 詢查所有節(jié)點,最后一個對端口無回應(yīng)的節(jié)點序號*/
struct in_addr node[31]; /* 用Tracert查到的路由節(jié)點地址 */
char f_send[30]; /* 向相應(yīng)節(jié)點成功發(fā)送TCP SYN包標志 ,成功置 1*/
charf_recv[30]; /* 成功接收各節(jié)點回復(fù)包標志,接收到置 1 */
int send_seq[30]; /* 發(fā)送的各SYN包的SEQ號,用來判斷接收包 */
unsigned char flag[30]; /* 如果接收返回包,保存返回包的TCP FLAG字段 */
}info_scan;
錯誤狀態(tài)如下:
#define ERR_PORTSCAN_ROUTE_NO 0x01
#define ERR_PORTSCAN_ROUTE_OK 0x02
#define ERR_PORTSCAN_ROUTE_HALF 0x03
#define ERR_PORTSCAN_SEND_SYN 0x11 //向網(wǎng)絡(luò)節(jié)點發(fā)送SYN同步包出錯
4 應(yīng)用案例
現(xiàn)有一計算機終端,無法登錄其開通的網(wǎng)絡(luò)多媒體點播服務(wù)系統(tǒng),但可以登錄其它網(wǎng)站,使用網(wǎng)絡(luò)測試儀的網(wǎng)絡(luò)故障診斷軟件來診斷該案例。
首先,通過用戶界面,填入多媒體點播系統(tǒng)的IP地址(如202.102.249.174)極其端口號(1026),然后點擊測試,診斷軟件首先查找從局域網(wǎng)絡(luò)到達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
然后,軟件將根據(jù)算法,從最后一個節(jié)點開始診斷,發(fā)現(xiàn)直到hn.kd.ny.adsl時,1026端口的連接測試不能通過,從而確定,問題是因為hn.kd.ny.adsl設(shè)備禁止了1026端口。向局端工程師確認,并修改多媒體登錄系統(tǒng)的端口為其它端口(8080),可以登錄,問題解決。
5 后記
使用該算法的網(wǎng)絡(luò)測試儀產(chǎn)品已經(jīng)研制成功,該產(chǎn)品同時具備了ping、sniffer等更多的網(wǎng)絡(luò)功能,可以更好的替代網(wǎng)絡(luò)維護人員隨聲攜帶的筆記本電腦和其它設(shè)備,簡便地進行網(wǎng)絡(luò)故障的診斷。
參考文獻:
[1](美)科默(Comer,D.E.),林瑤,蔣慧,等,譯.用TCP/IP進行網(wǎng)際互聯(lián)(第1卷):原理、協(xié)議與結(jié)構(gòu).北京:電子工業(yè)出版社,2001,5.
[2](美)W.Richard Stevens,范建華,等,譯.TCP/IP詳解.北京:機械工業(yè)出版社,2000,4.
[3](美)DonnaL.Harrington,童小林,等,譯.CCNP實戰(zhàn)指南:故障排除.北京:人民郵電出版社,2003,12.
[4](美)史蒂文斯,(美)芬納,(美)魯?shù)婪?楊繼張,譯. UNIX網(wǎng)絡(luò)編程.北京:清華大學(xué)出版社,2006,1.
收稿日期:2008-03-10
作者簡介:于長虹(1982-),男,網(wǎng)絡(luò)工程師,主要研究方向:網(wǎng)絡(luò)管理技術(shù);張偉鋒,洛陽師范學(xué)院信息技術(shù)學(xué)院。