【vfp精品源码栏目提醒】:网学会员vfp精品源码为您提供【精品】ping源码分析 - 其它资料参考,解决您在【精品】ping源码分析 - 其它资料学习中工作中的难题,参考学习。
ping
源码分析10.4.1 ping简介Ping是网络中应用非常广泛的一个软件,它是基于ICMP协议的。
下面首先对ICMP协议做一简单介绍。
ICMP是IP层的一个协议,它是用来探测主机、路由维护、路由选择和流量控制的。
ICMP报文的最终报宿不是报宿计算机上的一个用户进程,而是那个计算机上的IP层软件。
也就是说,当一个带有错误信息的ICMP报文到达时,IP软件模块就处理本身问题,而不把这个ICMP报文传送给应用程序。
ICMP报文类型有:回送ECHO回答(0);报宿不可 到达(3);报源断开(4);重定向(改变路由)(5);回送(ECHO)请求(8);数据 报超时 (11);数据报参数问题(12);时间印迹请求(13);时间印迹回答(14);信息请求(15);信息回答(16);地址掩码请求(17);地址掩码回答(18)。
虽然每种报文都有不同的格式,但它们开始都有下面三段: 一个8位整数报文TYPE(类型)段; 一个8位CODE(代码码)段,提供更多的报文类型信息; 一个16 位CHECKSUM(校验和)段;此外,报告差错的ICMP 报文还包含产生问题数据报的网际报头及前64 位数据。
一个ICMP回送请求与回送回答报文的格式如表10.17 所示。
表10.17 ICMP回送请求与回送回答报文格式类型CODE 校验和CHECKSUM标识符序列号《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程数据10.4.2 ping
源码分析下面的ping.c
源码是在busybox 里实现的
源码。
在这个完整的ping.c 代码中有较多选项的部分代码,因此,这里先分析除去选项部分代码的函数实现部分流程,接下来再给出完整的ping代码分析。
这样,读者就可以看到一个完整协议实现应该考虑到的各个部分。
1.Ping 代码主体流程Ping.c主体流程图如下图10.8 所示。
另外,由于ping是IP层的协议,因此在建立socket时需要使用SOCK_RAW 选项。
在循环等待回应信息处,用户可以指定“-f”洪泛选项,这时就会使用select函数来指定在一定的时间内进行回应。
2.主要选项说明Ping函数主要有以下几个选项: d:调试选项(F_SO_DEBUG) f:洪泛选项(F_FLOOD) i:等待选项(F_INTERVAL) r:路由选项(F_RROUTE) l:广播选项(MULTICAST_NOLOOP)对于这些选项,尤其是路由选项、广播选项和洪泛选项都会有不同的实现代码。
另外,ping 函数可以接受用户使用的SIGINT 和SIGALARM 信号来结束程序,它们分别指向了不同的结束代码,请读者阅读下面相关代码。
图10.8 ping主体流程图3.源代码及注释(1)主体代码ping代码的主体部分可以四部分,首先是一些头函数及宏定义:include ltsys/param.hgtinclude ltsys/socket.hgtinclude ltsys/file.hgtinclude ltsys/time.hgtinclude ltsys/signal.hgtinclude ltnetinet/in.hgtinclude ltnetinet/ip.hgtinclude ltnetinet/ip_icmp.hgtinclude ltarpa/inet.hgtinclude ltnetdb.hgtinclude ltunistd.hgtinclude ltstdlib.hgtinclude ltstring.hgtinclude ltstdio.hgtinclude ltctype.hgtinclude lterrno.hgtinclude ltgetopt.hgtinclude ltresolv.hgtdefine F_FLOOD 0x001define F_INTERVAL 0x002define F_NUMERIC 0x004define F_PINGFILLED 0x008define F_QUIET 0x010define F_RROUTE 0x020define F_SO_DEBUG 0x040define F_SO_DONTROUTE 0x080define F_VERBOSE 0x100/ 多播选项/int moptionsdefine MULTICAST_NOLOOP 0x001define MULTICAST_TTL 0x002define MULTICAST_IF 0x004…《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程接下来的第2 部分是建立socket并处理选项:Int mainint argc char argvstruct timeval timeoutstruct hostent hpstruct sockaddr_in tostruct protoent protostruct in_addr ifaddrint iint ch fdmask hold packlen preloadu_char datap packetchar target hnamebufMAXHOSTNAMELENu_char ttl loopint am_i_root…static char null NULL/__environ ampnull/am_i_root getuid0/建立socket连接,并且测试是否是root用户/if s socketAF_INET SOCK_RAW IPPROTO_ICMP lt 0 if errnoEPERM fprintfstderr quotping: ping must run as rootnquotelse perrorquotping: socketquotexit2…preload 0datap ampoutpack8 sizeofstruct timevalwhile ch getoptargc argv quotI:LRc:dfh:i:l:np:qrs:t:vquot EOFswitchch case c:npackets atoioptargif npackets lt 0 voidfprintfstderrquotping: bad number of packets to transmit.nquotexit2break/调用选项/case d:options F_SO_DEBUGbreak/flood选项/case f:if am_i_root voidfprintfstderrquotping: snquot strerrorEPERMexit2options F_FLOODsetbufstdout NULLbreak/等待选项/case i: / wait between sending packets /interval atoioptargif interval lt 0 voidfprintfstderrquotping: bad timing interval.nquotexit2options F_INTERVALbreakcase l:if am_i_root voidfprintfstderrquotping: snquot strerrorEPERMexit2preload atoioptargif preload lt 0 voidfprintfstderrquotping: bad preload value.nquot《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程exit2break…default:usageargc - optindargv optindif argc 1usagetarget argv接下来的第3 部分是用于获取地址,这里主要使用了inet_aton函数,将点分十进制地址转化为二进制地址。
当然,作为完整的ping程序有较完善的出错处理:memsetampwhereto 0 sizeofstruct sockaddrto struct sockaddr_in ampwheretoto-gtsin_family AF_INET/地址转换函数/if inet_atontarget ampto-gtsin_addr hostname targetelse if 0char addr resolve_nametarget 0if addr voidfprintfstderrquotping: unknown host snquot targetexit2to-gtsin_addr.s_addr inet_addraddrhostname targetelse/调用gethostbyname识别主机名/hp gethostbynametargetif hp voidfprintfstderrquotping: unknown host snquot targetexit2to-gtsin_family hp-gth_addrtypeif hp-gth_length gt intsizeofto-gtsin_addr hp-gth_length sizeofto-gtsin_addrmemcpyampto-gtsin_addr hp-gth_addr hp-gth_lengthvoidstrncpyhnamebuf hp-gth_name sizeofhnamebuf - 1hostname hnamebufendif接下来的一部分主要是对各个选项(如路由、多播)的处理,这里就不做介绍了。
再接下来是ping函数的最主要部分,就是接收无限循环回应信息,这里主要用到了函数recvfrom。
另外,对用户中断信息也有相应的处理,如下所示:if to-gtsin_family AF_INETvoidprintfquotPING s s: d data bytesnquot hostnameinet_ntoastruct in_addr ampto-gtsin_addr.s_addrdatalenelsevoidprintfquotPING s: d data bytesnquot hostname datalen/若程序接收到SIGINT或SIGALRM信号,调用相关的函数/voidsignalSIGINT finishvoidsignalSIGALRM catcher…/循环等待客户端的回应信息/for struct sockaddr_in fromregister int ccint fromlenif options amp F_FLOOD /形成ICMP回应数据包,在后面会有讲解/pinger/设定等待实践/timeout.tv_sec 0timeout.tv_usec 10000fdmask 1 ltlt s/调用select函数/《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程if selects 1 fd_set ampfdmask fd_set NULLfd_set NULL amptimeout lt 1continuefromlen sizeoffrom/接收客户端信息/if cc recvfroms char packet packlen 0struct sockaddr ampfrom ampfromlen lt 0 if errno EINTRcontinueperrorquotping: recvfromquotcontinuepr_packchar packet cc ampfromif npackets ampamp nreceived gt npacketsbreakfinish0/ NOTREACHED /return 0(2)其他函数下面的函数也是ping 程序中用到的重要函数。
首先catcher 函数是用户在发送SIGINT时调用的函数,在该函数中又调用了SIGALARM信号的处理来结束程序。
static voidcatcherint ignoreint waittimevoidignorepinger/调用catcher函数/voidsignalSIGALRM catcherif npackets ntransmitted lt npacketsalarmu_intintervalelse if nreceived waittime 2 tmax / 1000if waittimewaittime 1if waittime gt MAXWAITwaittime MAXWAIT elsewaittime MAXWAIT/调用finish函数,并设定一定的等待实践/voidsignalSIGALRM finishvoidalarmu_intwaittimePinger 函数也是一个非常重要的函数,用于形成ICMP回应数据包,其中ID 是该进程的ID,数据段中的前8 字节用于存放时间间隔,从而可以计算ping程序从对端返回的往返时延差,这里的数据校验用到了后面定义的in_cksum函数。
其代码如下所示:static voidpingervoidregister struct icmphdr icpregister int ccint i/形成icmp信息包,填写icmphdr结构体中的各项数据/icp struct icmphdr outpackicp-gticmp_type ICMP_ECHOicp-gticmp_code 0icp-gticmp_cksum 0icp-gticmp_seq ntransmittedicp-gticmp_id ident / ID /CLRicp-gticmp_seq mx_dup_ck/设定等待实践/if timingvoidgettimeofdaystruct timeval ampoutpack8struct timezone NULLcc datalen 8 / skips ICMP portion /《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程/ compute ICMP checksum here /icp-gticmp_cksum in_cksumu_short icp cci sendtos char outpack cc 0 ampwheretosizeofstruct sockaddrif i lt 0 i cc if i lt 0perrorquotping: sendtoquotvoidprintfquotping: wrote s d chars retdnquothostname cc iif options amp F_QUIET ampamp options amp F_FLOODvoidwriteSTDOUT_FILENO ampDOT 1pr_pack 是数据包显示函数,分别打印出IP 数据包部分和ICMP 回应信息。
在规范的程序中通常将数据的显示部分独立出来,这样就可以很好地加强程序的逻辑性和结构性。
voidpr_packchar buf int cc struct sockaddr_in fromregister struct icmphdr icpregister int iregister u_char cpdp/if 0/register u_long lregister int jstatic int old_rrlenstatic char old_rrMAX_IPOPTLEN/endif/struct iphdr ipstruct timeval tv tplong triptime 0int hlen dupflagvoidgettimeofdayamptv struct timezone NULL/ 检查IP数据包头信息/ip struct iphdr bufhlen ip-gtip_hl ltlt 2if cc lt datalen ICMP_MINLEN if options amp F_VERBOSEvoidfprintfstderrquotping: packet too short d bytes from snquot ccinet_ntoastruct in_addr ampfrom-gtsin_addr.s_addrreturn/ ICMP部分显示/cc - hlenicp struct icmphdr buf hlenif icp-gticmp_type ICMP_ECHOREPLY if icp-gticmp_id identreturn / Twas not our ECHO /nreceivedif timing ifndef icmp_datatp struct timeval icp 1elsetp struct timeval icp-gticmp_dataendiftvsubamptv tptriptime tv.tv_sec 10000 tv.tv_usec / 100tsum triptimeif triptime lt tmintmin triptimeif triptime gt tmaxtmax triptimeif TSTicp-gticmp_seq mx_dup_ck nrepeats--nreceiveddupflag 1 else SETicp-gticmp_seq mx_dup_ckdupflag 0《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程if options amp F_QUIETreturnif options amp F_FLOODvoidwriteSTDOUT_FILENO ampBSPACE 1else voidprintfquotd bytes from s: icmp_seququot ccinet_ntoastruct in_addr ampfrom-gtsin_addr.s_addricp-gticmp_seqvoidprintfquot ttldquot ip-gtip_ttlif timingvoidprintfquot timeld.ld msquot triptime/10triptime10if dupflagvoidprintfquot DUPquot/ check the data /ifndef icmp_datacp u_charicp 1 8elsecp u_charicp-gticmp_data 8endifdp ampoutpack8 sizeofstruct timevalfor i 8 i lt datalen i cp dp if cp dp voidprintfquotnwrong data byte d should be 0xx but was 0xxquoti dp cpcp u_charicp 1for i 8 i lt datalen i cp if i 32 8voidprintfquotntquotvoidprintfquotx quot cpbreak else / Weve got something other than an ECHOREPLY /if options amp F_VERBOSEreturnvoidprintfquotd bytes from s: quot ccpr_addrfrom-gtsin_addr.s_addrpr_icmphicp/if 0//显示其他IP选项/cp u_char buf sizeofstruct iphdrfor hlen gt intsizeofstruct iphdr --hlen cpswitch cp case IPOPT_EOL:hlen 0breakcase IPOPT_LSRR:voidprintfquotnLSRR: quothlen - 2j cpcpif j gt IPOPT_MINOFFfor l cpl lltlt8 cpl lltlt8 cpl lltlt8 cpif l 0voidprintfquott0.0.0.0quotelsevoidprintfquottsquot pr_addrntohllhlen - 4j - 4if j lt IPOPT_MINOFFbreakvoidputcharnbreakcase IPOPT_RR:《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程j cp / get length /i cp / and pointer /hlen - 2if i gt ji ji - IPOPT_MINOFFif i lt 0continueif i old_rrlenampamp cp u_char buf sizeofstruct iphdr 2ampamp memcmpchar cp old_rr iampamp options amp F_FLOOD voidprintfquottsame routequoti i 3 / 4 4hlen - icp ibreakold_rrlen imemcpyold_rr cp ivoidprintfquotnRR: quotfor l cpl lltlt8 cpl lltlt8 cpl lltlt8 cpif l 0voidprintfquott0.0.0.0quotelsevoidprintfquottsquot pr_addrntohllhlen - 4i - 4if i lt 0breakvoidputcharnbreakcase IPOPT_NOP:voidprintfquotnNOPquotbreakdefault:voidprintfquotnunknown option xquot cpbreak/endif/if options amp F_FLOOD voidputcharnvoidfflushstdoutin_cksum是数据校验程序,如下所示:static intin_cksumu_short addr int lenregister int nleft lenregister u_short w addrregister int sum 0u_short answer 0/这里的算法很简单,就采用32bit的加法/while nleft gt 1 sum wnleft - 2if nleft 1 u_char ampanswer u_char w sum answer/把高16bit加到低16bit上去/sum sum gtgt 16 sum amp 0xffffsum sum gtgt 16answer sumreturnanswer《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程Finish程序是ping程序的结束程序,主要是打印出来一些统计信息,如下所示:static voidfinishint ignorevoidignorevoidsignalSIGINT SIG_IGNvoidputcharnvoidfflushstdoutvoidprintfquot--- s ping statistics ---nquot hostnamevoidprintfquotld packets transmitted quot ntransmittedvoidprintfquotld packets received quot nreceivedif nrepeatsvoidprintfquotld duplicates quot nrepeatsif ntransmittedif nreceived gt ntransmittedvoidprintfquot-- somebodys printing up packetsquotelsevoidprintfquotd packet lossquotint ntransmitted - nreceived 100 /ntransmittedvoidputcharnif nreceived ampamp timingvoidprintfquotround-trip min/avg/max ld.ld/lu.ld/ld.ldmsnquottmin/10 tmin10tsum / nreceived nrepeats/10tsum / nreceived nrepeats10tmax/10 tmax10if nreceived0 exit1exit0ifdef notdefstatic char ttab quotEcho Replyquot / ip seq udata /quotDest Unreachablequot / net host proto port frag sr IP /quotSource Quenchquot / IP /quotRedirectquot / redirect 类型 gateway IP /quotEchoquotquotTime Exceededquot /传输超时/quotParameter Problemquot / IP参数问题/quotTimestampquot / id seq three timestamps /quotTimestamp Replyquot / quot /quotInfo Requestquot / id sq /quotInfo Replyquot / quot /endifpr_icmph函数是用于打印ICMP的回应信息,如下所示:static voidpr_icmphstruct icmphdr icpswitchicp-gticmp_type /ICMP回应/case ICMP_ECHOREPLY:voidprintfquotEcho Replynquot/ XXX ID Seq Data /break/ICMP终点不可达/case ICMP_DEST_UNREACH:switchicp-gticmp_code case ICMP_NET_UNREACH:voidprintfquotDestination Net Unreachablenquotbreakcase ICMP_HOST_UNREACH:voidprintfquotDestination Host Unreachablenquotbreakcase ICMP_PROT_UNREACH:voidprintfquotDestination Protocol Unreachablenquotbreak…default:voidprintfquotDest Unreachable Unknown Code: dnquoticp-gticmp_codebreak《嵌入式Linux应用程序开发详解》——第10章、嵌入式Linux网络编程/ Print returned IP header information /ifndef icmp_datapr_retipstruct iphdr icp 1elsepr_retipstruct iphdr icp-gticmp_dataendifbreak…default:voidprintfquotRedirect Bad Code: dquot icp-gticmp_codebreakvoidprintfquotNew addr: snquotinet_ntoaicp-gticmp_gwaddrifndef icmp_datapr_retipstruct iphdr icp 1elsepr_retipstruct iphdr icp-gticmp_dataendifbreakcase ICMP_ECHO:voidprintfquotEcho Requestnquot/ XXX ID Seq Data /breakcase ICMP_TIME_EXCEEDED:switchicp-gticmp_code case ICMP_EXC_TTL:voidprintfquotTime to live exceedednquotbreakcase ICMP_EXC_FRAGTIME:voidprintfquotFrag reassembly time exceedednquotbreakdefault:voidprintfquotTime exceeded Bad Code: dnquoticp-gticmp_codebreak…default:voidprintfquotBad ICMP type: dnquot icp-gticmp_typepr_iph函数是用于打印IP数据包头选项,如下所示:static voidpr_iphstruct iphdr ipint hlenu_char cphlen ip-gtip_hl ltlt 2cp u_char ip 20 / point to options /voidprintfquotVr HL TOS Len ID Flg off TTL Pro cks Src Dst Datanquotvoidprintfquot 1x 1x 02x 04x 04xquotip-gtip_v ip-gtip_hl ip-gtip_tos ip-gtip_len ip-gtip_idvoidprintfquot 1x 04xquot ip-gtip_off amp 0xe000 gtgt 13ip-gtip_off amp 0x1fffvoidprintfquot 02x 02x 04xquot ip-gtip_ttl ip-gtip_p ip-gtip_sumvoidprintfquot s quot inet_ntoastruct in_addr ampip-gtip_srcvoidprintfquot s quot inet_ntoastruct in_addr ampip-gtip_dst/ dump and option bytes /while hlen-- gt 20 voidprintfquot02xquot cpvoidputcharnpr_addr 是用于将ascii 主机地址转换为十进制点分形式并打印出来,这里使用的函数是inet_ntoa,如下所示:static char pr_addru_long lstruct hostent hpstatic.
上一篇:
2013年二级VFP无纸化题库
下一篇:
谈谈软件工程标准如何实施