第六章ICMP不可达差错

上传人:cl****1 文档编号:586742607 上传时间:2024-09-05 格式:PPT 页数:46 大小:1.91MB
返回 下载 相关 举报
第六章ICMP不可达差错_第1页
第1页 / 共46页
第六章ICMP不可达差错_第2页
第2页 / 共46页
第六章ICMP不可达差错_第3页
第3页 / 共46页
第六章ICMP不可达差错_第4页
第4页 / 共46页
第六章ICMP不可达差错_第5页
第5页 / 共46页
点击查看更多>>
资源描述

《第六章ICMP不可达差错》由会员分享,可在线阅读,更多相关《第六章ICMP不可达差错(46页珍藏版)》请在金锄头文库上搜索。

1、ICMP不可达差错(需要分片) 发生I C M P不可达差错的另一种情况是,当路由器收到一份需要分片的数据报,而在 I P首部又设置了不分片(D F)的标志比特。如果某个程序需要判断到达目的端的路途中最小 M T U是多少称作路径M T U发现机制(2 . 9节) ,那么这个差错就可以被该程序使用。 这种情况下的I C M P不可达差错报文格式如图 11 - 9所示。这里的格式与图6 - 1 0不同,因为在第2个32 bit字中,1631 bit可以提供下一站的M T U,而不再是0。 如果路由器没有提供这种新的ICMP差错报文格式,那么下一站的MTU就设为0。 新版的路由器需求RFC Alm

2、quist 1993声明,在发生这种I C M P不可达差错时,路由器必须生成这种新格式的报文。例子关于分片作者曾经遇到过一个问题,I C M P差错试图判断从路由器n e t b到主机s u n之间的拨号S L I P链路的M T U。我们知道从s u n到n e t b的链路的M T U:当S L I P被安装到主机s u n时,这是S L I P配置过程中的一部分,加上在3 . 9节中已经通过n e t s t a t命令观察过。现在,我们想从另一个方向来判断它的M T U(在第2 5章,将讨论如何用S N M P来判断) 。在点到点的链路中,不要求两个方向的M T U为相同值。所采用的

3、技术是在主机s o l a r i s上运行p i n g程序到主机b s d i,增加数据分组长度,直到看见进入的分组被分片为止。如图11 - 1 0所示。在主机s u n上运行t c p d u m p,观察S L I P链路,看什么时候发生分片。开始没有观察到分片,一切都很正常直到p i n g分组的数据长度从5 0 0增加到6 0 0字节。可以看到接收到的回显请求(仍然没有分片) ,但不见回显应答。为了跟踪下去,也在主机 b s d i上运行t c p d u m p,观察它接收和发送的报文。输出如图11 - 11所示。首先,每行中的标记(D F)说明在I P首部中设置了不分片比特。这

4、意味着 Solaris 2.2 一般把不分片比特置1,作为实现路径M T U发现机制的一部分。第1行显示的是回显请求通过路由器 n e t b到达s u n主机,没有进行分片,并设置了 D F比特,因此我们知道还没有达到n e t b的SLIP MTU。接下来,在第2行注意到D F标志被复制到回显应答报文中。这就带来了问题。回显应答与回显请求报文长度相同(超过 6 0 0字节) ,但是s u n外出的S L I P接口M T U为5 5 2。因此回显应答需要进行分片,但是D F标志比特又被设置了。这样,s u n就产生一个I C M P不可达差错报文返回给b s d i(报文在b s d i处

5、被丢弃) 。这就是我们在主机 s o l a r i s上没有看到任何回显应答的原因。这些应答永远不能通过s u n。分组的路径如图11 - 1 2所示。最后,在图11 - 11中的第3行和第6行中,m t u = 0表示主机s u n没有在I C M P不可达报文中返回出口M T U值,如图11 - 9所示(在2 5 . 9节中,将重新回到这个问题,用 S N M P判断n e t b上的S L I P接口M T U值为1 5 0 0) 。 用Traceroute确定路径MTU尽管大多数的系统不支持路径 M T U发现功能,但可以很容易地修改 t r a c e r o u t e程序(第8

6、章) ,用它来确定路径M T U。要做的是发送分组,并设置“不分片”标志比特。发送的第一个分组的长度正好与出口M T U相等,每次收到I C M P“不能分片”差错时(在上一节讨论的)就减小分组的长度。如果路由器发送的 I C M P差错报文是新格式,包含出口的M T U,那么就用该M T U值来发送,否则就用下一个最小的 M T U值来发送。正如 RFC 1191 Mogul andDeering 1990声明的那样,M T U值的个数是有限的,因此在我们的程序中有一些由近似值构成的表,取下一个最小M T U值来发送。首先,我们尝试判断从主机sun到主机slip的路径MTU,知道SLIP链路

7、的MTU为2 9 6。在这个例子中,路由器 b s d i没有在I C M P差错报文中返回出口 M T U,因此我们选择另一个M T U近似值。T T L为2的第1行输出打印的主机名为b s d i,但这是因为它是返回I C M P差错报文的路由器。T T L为2的最后一行正是我们所要找的。在b s d i上修改I C M P代码使它返回出口M T U值并不困难,如果那样做并再次运行该程序得到如下输出结果:这时,在找到正确的M T U值之前,我们不用逐个尝试 8个不同的M T U值路由器返回了正确的M T U值。 采用UDP的路径MTU发现下面对使用U D P的应用程序与路径M T U发现机

8、制之间的交互作用进行研究。看一看如果应用程序写了一个对于一些中间链路来说太长的数据报时会发生什么情况。由于我们所使用的支持路径M T U发现机制的唯一系统就是Solaris 2.x,因此,将采用它作为源站发送一份6 5 0字节数据报经s l i p。由于s l i p主机位于M T U为2 9 6的S L I P链路后,因此,任何长于2 6 8字节(2 9 62 08)且“不分片”比特置为1的U D P数据都会使b s d i路由器产生I C M P“不能分片”差错报文。图11 - 1 3给出了拓扑结构和M T U。可以用下面的命令行来产生650字节UDP数据报,每两个UDP数据报之间的间隔是

9、5秒:solaris % sock -u -i -n10 -w650 -p5 slip discard图11 - 1 4是t c p d u m p的输出结果。在运行这个例子时,将b s d i设置成在I C M P“不能分片”差错中,不返回下一跳M T U信息。在发送的第一个数据报中将 D F比特置1(第1行) ,其结果是从b s d i路由器发回我们可以猜测的结果(第2行) 。令人不解的是,发送一个 D F比特置1的数据报(第3行) ,其结果是同样的I C M P差错(第4行) 。我们预计这个数据报在发送时应该将D F比特置0。第5行结果显示,I P已经知道了发往该目的地址的数据报不能将

10、D F比特置1,因此,I P进而将数据报在源站主机上进行分片。这与前面的例子中, I P发送经过U D P的数据报,允许具有较小M T U的路由器(在本例中是b s d i)对它进行分片的情况不一样。由于 I C M P“不能分片”报文并没有指出下一跳的M T U,因此,看来I P猜测M T U为5 7 6就行了。第一次分片(第5行)包含5 4 4字节的U D P数据、8字节U D P首部以及2 0字节I P首部,因此,总I P数据报长度是5 7 2字节。第2次分片(第6行)包含剩余的1 0 6字节U D P数据和2 0字节I P首部。不幸的是,第7行的下一个数据报将其D F比特置1,因此b

11、s d i将它丢弃并返回I C M P差错。这时发生了I P定时器超时,通知 I P查看是不是因为路径 M T U增大了而将D F比特再一次置1。我们可以从第1 9行和2 0行看出这个结果。将第7行与1 9行进行比较,可以看出I P每过3 0秒就将D F比特置1,以查看路径M T U是否增大了。这个3 0秒的定时器值看来太短。 R F C 11 9 1建议其值取 1 0分钟。可以通过修改i p _ i r e _ p a t h m t u _ i n t e r v a l(E . 4节)参数来改变该值。同时,Solaris 2.2无法对单个U D P应用或所有U D P应用关闭该路径M T

12、 U发现。只能通过修改i p _ p a t h _ m t u _ d i s c o v e r y参数,在系统一级开放或关闭它。正如在这个例子里所能看到的那样,如果允许路径 M T U发现,那么当U D P应用程序写入可能被分片数据报时,该数据报将被丢弃。s o l a r i s的I P层所假设的最大数据报长度( 5 7 6字节)是不正确的。在图 11 - 1 3中,我们看到,实际的M T U值是2 9 6字节。这意味着经s o l a r i s分片的数据报还将被 b s d i分片。图11 - 1 5给出了在目的主机(s l i p)上所收集到的t c p d u m p对于第一个

13、到达数据报的输出结果(图11 - 1 4的第5行和第6行) 。在本例中,s o l a r i s不应该对外出数据报分片,它应该将 D F比特置0,让具有最小M T U的路由器来完成分片工作。现在我们运行同一个例子,只是对路由器 b s d i进行修改使其在I C M P“不能分片”差错中返回下一跳M T U。图11 - 1 6给出了t c p d u m p输出结果的前6行。UDP和ARP之间的交互作用使用U D P,可以看到U D P与A R P典型实现之间的有趣的(而常常未被人提及)交互作用。我们用s o c k程序来产生一个包含8 1 9 2字节数据的U D P数据报。预测这将会在以太

14、网上产生6个数据报片(见习题 11 . 3) 。同时也确保在运行该程序前, A R P缓存是清空的,这样,在发送第一个数据报片前必须交换A R P请求和应答。bsdi % arp -a 验证A R P高速缓存是空的bsdi % sock -u -i -nl -w8192 svr4 discard预计在发送第一个数据报片前会先发送一个 A R P请求。I P还会产生5个数据报片,这样就提出了我们必须用t c p d u m p来回答的两个问题:在接收到 A R P回答前,其余数据报片是否已经做好了发送准备?如果是这样,那么在 A R P等待应答时,它会如何处理发往给定目的的多个报文?图11 -

15、1 7给出了t c p d u m p的输出结果。在这个输出结果中有一些令人吃惊的结果。首先,在第一个 A R P应答返回以前,总共产生了6个A R P请求。我们认为其原因是 I P很快地产生了6个数据报片,而每个数据报片都引发了一个A R P请求。第二,在接收到第一个A R P应答时(第7行) ,只发送最后一个数据报片(第 9行)!看来似乎将前5个数据报片全都丢弃了。实际上,这是 A R P的正常操作。在大多数的实现中,在等待一个A R P应答时,只将最后一个报文发送给特定目的主机。Host Requirements RFC要求实现中必须防止这种类型的A R P洪泛(ARP flooding

16、,即以高速率重复发送到同一个I P地址的A R P请求) 。建议最高速率是每秒一次。而这里却在4.3 ms内发出了6个ARP请求。Host Requirements RFC规定,ARP应该保留至少一个报文,而这个报文必须是最后一个报文。这正是我们在这里所看到的结果。另一个无法解释的不正常的现象是,s v r 4发回7个,而不是6个A R P应答。最后要指出的是,在最后一个 A R P应答返回后,继续运行 t c p d u m p程序5分钟,以看看s v r 4是否会返回I C M P“组装超时”差错。并没有发送 I C M P差错(我们在图8 - 2中给出了该消息的格式。c o d e字段为

17、1表示在重新组装数据报时发生了超时) 。在第一个数据报片出现时, I P层必须启动一个定时器。这里“第一个”表示给定数据报的第一个到达数据报片,而不是第一个数据报片(数据报片偏移为 0) 。正常的定时器值为3 0或6 0秒。如果定时器超时而该数据报的所有数据报片未能全部到达,那么将这些数据报片丢弃。如果不这么做,那些永远不会到达的数据报片(正如我们在本例中所看到的那样)迟早会引起接收端缓存满。这里我们没看到I C M P消息的原因有两个。首先,大多数从 B e r k e l e y派生的实现从不产生该差错!这些实现会设置定时器,也会在定时器溢出时将数据报片丢弃,但是不生成 I C M P差错

18、。第二,并未接收到包含 U D P首部的偏移量为0的第一个数据报片(这是被 A R P所丢弃的5个报文的第1个) 。除非接收到第一个数据报片,否则并不要求任何实现产生 I C M P差错。其原因是因为没有运输层首部,I C M P差错的接收者无法区分出是哪个进程所发送的数据报被丢弃。这里假设上层(T C P或使用U D P的应用程序)最终会超时并重传。在本节中,我们使用 I P数据报片来查看U D P与A R P之间的交互作用。如果发送端迅速发送多个U D P数据报,也可以看到这个交互过程。我们选择采用分片的方法,是因为 I P可以生成报文的速度,比一个用户进程生成多个数据报的速度更快。尽管本

19、例看来不太可能,但它确实经常发生。 N F S发送的U D P数据报长度超过8 1 9 2字节。在以太网上,这些数据报以我们所指出的方式进行分片,如果适当的 A R P缓存入口发生超时,那么就可以看到这里所显示的现象。N F S将超时并重传,但是由于A R P的有限队列,第一个I P数据报仍可能被丢弃。 最大UDP数据报长度理论上,I P数据报的最大长度是6 5 5 3 5字节,这是由I P首部(图3 - 1)1 6比特总长度字段所限制的。去除2 0字节的I P首部和8个字节的U D P首部,U D P数据报中用户数据的最长长度为6 5 5 0 7字节。但是,大多数实现所提供的长度比这个最大值

20、小。我们将遇到两个限制因素。第一,应用程序可能会受到其程序接口的限制。 socket API提供了一个可供应用程序调用的函数,以设置接收和发送缓存的长度。对于 UDP socket,这个长度与应用程序可以读写的最大 U D P数据报的长度直接相关。现在的大部分系统都默认提供了可读写大于8 1 9 2字节的U D P数据报(使用这个默认值是因为 8 1 9 2是N F S读写用户数据数的默认值) 。第二个限制来自于T C P / I P的内核实现。可能存在一些实现特性(或差错) ,使I P数据报长度小于6 5 5 3 5字节。作者使用s o c k程序对不同U D P数据报长度进行了试验。在Su

21、nOS 4.1.3下使用环回接口的最大I P数据报长度是3 2 7 6 7字节。比它大的值都会发生差错。但是从B S D / 3 8 6到SunOS 4.1.3的情况下,S u n所能接收到最大I P数据报长度为3 2 7 8 6字节(即3 2 7 5 8字节用户数据) 。在Solaris 2.2下使用环回接口,最大可收发I P数据报长度为6 5 5 3 5字节。从Solaris 2.2到AIX 3.2.2,发送的最大IP数据报长度可以是65535字节。很显然,这个限制与源端和目的端的实现有关。我们在3 . 2节中提过,要求主机必须能够接收最短为 5 7 6字节的I P数据报。在许多U D P

22、应用程序的设计中,其应用程序数据被限制成 5 1 2字节或更小,因此比这个限制值小。例如,我们在1 0 . 4节中看到,路径信息协议总是发送每份数据报小于 5 1 2字节的数据。我们还会在其他U D P应用程序如D N S(第1 4章) 、T F T P(第1 5章) 、B O O T P(第1 6章)以及S N M P(第2 5章)中遇到这个限制。由于I P能够发送或接收特定长度的数据报并不意味着接收应用程序可以读取该长度的数据。因此,U D P编程接口允许应用程序指定每次返回的最大字节数。如果接收到的数据报长度大于应用程序所能处理的长度,那么会发生什么情况呢?不幸的是,该问题的答案取决于编

23、程接口和实现。典型的B e r k e l e y版socket API对数据报进行截断,并丢弃任何多余的数据。应用程序何时能够知道,则与版本有关(4.3BSD Reno及其后的版本可以通知应用程序数据报被截断) 。S V R 4下的socket API(包括Solaris 2.x) 并不截断数据报。超出部分数据在后面的读取中返回。它也不通知应用程序从单个UDP数据报中多次进行读取操作。TLI API不丢弃数据。相反,它返回一个标志表明可以获得更多的数据,而应用程序后面的读操作将返回数据报的其余部分。在讨论T C P时,我们发现它为应用程序提供连续的字节流,而没有任何信息边界。 T C P以应

24、用程序读操作时所要求的长度来传送数据,因此,在这个接口下,不会发生数据丢失。ICMP源站抑制差错我们同样也可以使用U D P产生I C M P“源站抑制(source quench)”差错。当一个系统(路由器或主机)接收数据报的速度比其处理速度快时,可能产生这个差错。注意限定词“可能” 。即使一个系统已经没有缓存并丢弃数据报,也不要求它一定要发送源站抑制报文。图11 - 1 8给出了I C M P源站抑制差错报文的格式。有一个很好的方案可以在我们的测试网络里产生该差错报文。可以从 b s d i通过必须经过拨号S L I P链路的以太网,将数据报发送给路由器s u n。由于S L I P链路的

25、速度大约只有以太网的千分之一,因此,我们很容易就可以使其缓存用完。下面的命令行从主机 b s d i通过路由器s u n发送1 0 0个1 0 2 4字节长数据报给s o l a r i s。我们将数据报发送给标准的丢弃服务,这样,这些数据报将被忽略:bsdi % sock -u -i -w1024 -n100 solaris discard图11 - 1 9给出了与此命令行相对应的t c p d u m p输出结果在这个输出结果中,删除了很多行,这只是一个模型。接收前 2 6个数据报时未发生差错;我们只给出了第一个数据报的结果。然而,从第 2 7个数据报开始,每发送一份数据报,就会接收到一份

26、源站抑制差错报文。总共有 26 +742)= 174行输出结果。从2 . 1 0节的并行线吞吐率计算结果可以知道,以 9600 b/s速率传送1 0 2 4字节数据报只需要1秒时间(由于从s u n到n e t b的S L I P链路的M T U为5 5 2字节,因此在我们的例子中,20 + 8 +1 0 2 4字节数据报将进行分片,因此,其时间会稍长一些) 。但是我们可以从图11 - 1 9的时间中看出,s u n路由器在不到1秒时间内就处理完所有的 1 0 0个数据报,而这时,第一份数据报还未通过S L I P链路。因此我们用完其缓存就不足不奇了。尽管RFC 1009 Braden and

27、 Postel 1987 要求路由器在没有缓存时产生源站抑制差错报文,但是新的Router Requirements RFC Almquist 1993 对此作了修改,提出路由器不应该产生源站抑制差错报文。由于源站抑制要消耗网络带宽,且对于拥塞来说是一种无效而不公平的调整,因此现在人们对于源站抑制差错的态度是不支持的。在本例中,还需要指出的是, s o c k程序要么没有接收到源站抑制差错报文,要么接收到却将它们忽略了。结果是如果采用 U D P协议,那么B S D实现通常忽略其接收到的源站抑制报文(正如我们在2 1 . 1 0节所讨论的那样,T C P接受源站抑制差错报文,并将放慢在该连接上

28、的数据传输速度) 。其部分原因在于,在接收到源站抑制差错报文时,导致源站抑制的进程可能已经中止了。实际上,如果使用Unix 的t i m e程序来测定s o c k程序所运行的时间,其结果是它只运行了大约0 . 5秒时间。但是从图11 - 1 9中可以看到,在发送第一份数据报过后 0 . 7 1秒才接收到一些源站抑制,而此时该进程已经中止。其原因是我们的程序写入了 1 0 0个数据报然后中止了。但是所有的1 0 0个数据报都已发送出去有一些数据报在输出队列中。这个例子重申了U D P是一个非可靠的协议,它说明了端到端的流量控制。尽管 s o c k程序成功地将1 0 0个数据报写入其网络,但只

29、有2 6个数据报真正发送到了目的端。其他 7 4个数据报可能被中间路由器丢弃。除非在应用程序中建立一些应答机制,否则发送端并不知道接收端是否收到了这些数据。UDP服务器的设计使用U D P的一些蕴含对于设计和实现服务器会产生影响。通常,客户端的设计和实现比服务器端的要容易一些,这就是我们为什么要讨论服务器的设计,而不是讨论客户端的设计的原因。典型的服务器与操作系统进行交互作用,而且大多数需要同时处理多个客户。通常一个客户启动后直接与单个服务器通信,然后就结束了。而对于服务器来说,它启动后处于休眠状态,等待客户请求的到来。对于 U D P来说,当客户数据报到达时,服务器苏醒过来,数据报中可能包含

30、来自客户的某种形式的请求消息。在这里我们所感兴趣的并不是客户和服务器的编程方面( Stevens 1990对这些方面的细节进行了讨论) ,而是U D P那些影响使用该协议的服务器的设计和实现方面的协议特性(我们在 1 8 . 11节中对T C P服务器的设计进行了描述) 。尽管我们所描述的一些特性取决于所使用U D P的实现,但对于大多数实现来说,这些特性是公共的。 客户IP地址及端口号来自客户的是U D P数据报。I P首部包含源端和目的端 I P地址,U D P首部包含了源端和目的端的U D P端口号。当一个应用程序接收到 U D P数据报时,操作系统必须告诉它是谁发送了这份消息,即源I

31、P地址和端口号。这个特性允许一个交互 U D P服务器对多个客户进行处理。给每个发送请求的客户发回应答。 目的IP地址一些应用程序需要知道数据报是发送给谁的,即目的 I P地址。例如,Host Requirements R F C规定,T F T P服务器必须忽略接收到的发往广播地址的数据报(我们分别在第 1 2章和第1 5章对广播和T F T P进行描述) 。这要求操作系统从接收到的 U D P数据报中将目的I P地址交给应用程序。不幸的是,并非所有的实现都提供这个功能。socket API以I P _ R E C V D S TADDR socket选项提供了这个功能。对于本文中使用的系统

32、,只有B S D / 3 8 6、4 . 4 B S D和AIX 3.2.2支持该选项。S V R 4、SunOS 4.x和Solaris 2.x都不支持该选项。UDP输入队列我们在1 . 8节中说过,大多数U D P服务器是交互服务器。这意味着,单个服务器进程对单个U D P端口上(服务器上的名知端口)的所有客户请求进行处理。通常程序所使用的每个 U D P端口都与一个有限大小的输入队列相联系。这意味着,来自不同客户的差不多同时到达的请求将由 U D P自动排队。接收到的U D P数据报以其接收顺序交给应用程序(在应用程序要求交送下一个数据报时) 。然而,排队溢出造成内核中的 U D P模块

33、丢弃数据报的可能性是存在的。可以进行以下试验。我们在作为U D P服务器的b s d i主机上运行s o c k程序:bsdi % sock -s -u -v -E -R256 -P30 6666from 140.252.13.33, to 140.252.13.63: 1111111111 从s u n发送到广播地址from 140.252.13.34, to 140.252.13.35: 4444444444444 从s v r 4发送到单播地址我们指明以下标志: - s表示作为服务器运行, - u表示U D P,- v表示打印客户的 I P地址,- E表示打印目的I P地址(该系统支持这

34、个功能) 。另外,我们将这个端口的U D P接收缓存设置为2 5 6字节(- R) ,其每次应用程序读取的大小也是这个数( - r) 。标志- P 3 0表示创建U D P端口后,先暂停3 0秒后再读取第一个数据报。这样,我们就有时间在另两台主机上启动客户程序,发送一些数据报,以查看接收队列是如何工作的。服务器一开始工作,处于其 3 0秒的暂停时间内,我们就在 s u n主机上启动一个客户,并发送三个数据报:sun % sock -u -v 140.252.13.63 6666 到以太网广播地址connected on 140.252.13.33.1252 to 140.252.13.63.6

35、6661 1 1 1 1 1 1 1 1 1 1 1字节的数据(新行)2 2 2 2 2 2 2 2 2 1 0字节的数据(新行)3 3 3 3 3 3 3 3 3 3 3 1 2字节的数据(新行)目的地址是广播地址(1 4 0 . 2 5 2 . 1 3 . 6 3) 。我们同时也在主机s v r 4上启动第2个客户,并发送另外三个数据报:svr4 % sock -u -v bsdi 6666connected on 0.0.0.0.1042 to 140.252.13.35.66664 4 4 4 4 4 4 4 4 4 4 4 4 1 4字节的数据(新行)5 5 5 5 5 5 5 5

36、5 5 5 5 5 5 5 1 6字节的数据(新行)6 6 6 6 6 6 6 6 9字节的数据(新行)首先,我们早些时候在 b s d i上所看到的结果表明,应用程序只接收到 2个数据报:来自s u n的第一个全1报文,和来自s v r 4的第一个全4报文。其他4个数据报看来全被丢弃。图11 - 2 0给出的t c p d u m p输出结果表明,所有6个数据报都发送给了目的主机。两个客户的数据报以交替顺序键入:第一个来自 s u n,然后是来自s v r 4的,以此类推。同时也可以看出,全部6个数据报大约在1 2秒内发送完毕,也就是在服务器休眠的3 0秒内完成的。我们还可以看到,服务器的-

37、 E选项使其可以知道每个数据报的目的 I P地址。如果需要,它可以选择如何处理其接收到的第一个数据报,这个数据报的地址是广播地址。我们可以从本例中看到以下几个要点。首先,应用程序并不知道其输入队列何时溢出。只是由U D P对超出数据报进行丢弃处理。同时,从 t c p d u m p输出结果,我们看到,没有发回任何信息告诉客户其数据报被丢弃。这里不存在像 I C M P源站抑制这样发回发送端的消息。最后,看来U D P输出队列是F I F O(先进先出)的,而我们在 11 . 9节中所看到的 A R P输入却是 限制本地IP地址大多数U D P服务器在创建U D P端点时都使其本地I P地址具

38、有通配符( w i l d c a r d )的特点。这就表明进入的U D P数据报如果其目的地为服务器端口,那么在任何本地接口均可接收到它。例如,我们以端口号7 7 7启动一个U D P服务器:sun % sock -u -s 7777然后,用n e t s t a t命令观察端点的状态:sun % netstat -a -n -f inetActive Internet connections (including servers)Proto Recv-Q Send-Q Local Address Foreign Address (state)udp 0 0 *.7777 *.*这里,我们

39、删除了许多行,只保留了其中感兴趣的东西。 - a选项表示报告所有网络端点的状态。- n选项表示以点数格式打印 I P地址而不用D N S把地址转换成名字,打印数字端口号而不是服务名称。-f inet选项表示只报告T C P和U D P端点。本地地址以* . 7 7 7 7格式打印,星号表示任何本地I P地址。当服务器创建端点时,它可以把其中一个主机本地 I P地址包括广播地址指定为端点的本地I P地址。只有当目的I P地址与指定的地址相匹配时,进入的 U D P数据报才能被送到这个端点。用我们的s o c k程序,如果在端口号之前指定一个 IP地址,那么该IP地址就成为该端点的本地IP地址。例

40、如:sun % sock -u -s 140.252.1.29 7777就限制服务器在S L I P接口( 1 4 0 . 2 5 2 . 1 . 2 9 )处接收数据报。n e t s t a t输出结果显示如下:Proto Recv-Q Send-Q Local Address Foreign Address (state)udp 0 0 140.252.1.29.7777 *.*如果我们试图在以太网上的主机 b s d i以地址1 4 0 . 2 5 2 . 1 3 . 3 5向该服务器发送一份数据报,那么将返回一个I C M P端口不可达差错。服务器永远看不到这份数据报。这种情形如图

41、11 - 2 1所示。有可能在相同的端口上启动不同的服务器,每个服务器具有不同的本地 I P地址。但是,一般必须告诉系统应用程序重用相同的端口号没有问题。使用sockets API时,必须指定S O _ R E U S E A D D Rs o c k e t选项。在s o c k程序中是通过-A选项来完成的。除了第一个以外,其他的服务器都必须以- A选项启动,告诉系统可以重用同一个端口号。5个服务器的n e t s t a t输出结果如下所示:在这种情况下,到达服务器的数据报中,只有带星号的本地 I P地址,其目的地址为1 4 0 . 2 5 2 . 1 . 2 5 5,因为其他4个服务器占

42、用了其他所有可能的I P地址。如果存在一个含星号的 I P地址,那么就隐含了一种优先级关系。如果为端点指定了特定I P地址,那么在匹配目的地址时始终优先匹配该 I P地址。只有在匹配不成功时才使用含星号的端点。 限制远端IP地址在前面所有的n e t s t a t输出结果中,远端I P地址和远端端口号都显示为 * . *,其意思是该端点将接受来自任何I P地址和任何端口号的U D P数据报。大多数系统允许 U D P端点对远端地址进行限制。这说明端点将只能接收特定 I P地址和端口号的U D P数据报。s o c k程序用- f选项来指定远端I P地址和端口号:sun % sock -u -

43、s -f 140.252.13.35.4444 5555这样就设置了远端I P地址1 4 0 . 2 5 2 . 1 3 . 3 5(即主机b s d i)和远端端口号4444 。服务器的有名端口号为5 5 5 5。如果运行n e t s t a t命令,我们发现本地I P地址也被设置了,尽管我们没有指定。Proto Recv-Q Send-Q Local Address Foreign Address (state)udp 0 0 140.252.13.33.5555 140.252.13.35.4444这是在伯克利派生系统中指定远端 I P地址和端口号带来的副作用:如果在指定远端地址时没有

44、选择本地地址,那么将自动选择本地地址。它的值就成为选择到达远端 I P地址路由时将选择的接口 I P地址。事实上,在这个例子中, s u n在以太网上的 I P地址与远端地址1 4 0 . 2 5 2 . 1 3 . 3 3相连。图11 - 2 2总结了U D P服务器本身可以创建的三类地址绑定。在所有情况下,l p o r t指的是服务器有名端口号,l o c a l I P必须是本地接口的I P地址。表中这三行的排序是U D P模块在判断用哪个端点接收数据报时所采用的顺序。最为确定的地址(第一行)首先被匹配,最不确定的地址(最后一行 I P地址带有两个星号)最后进行匹配。 每个端口有多个接

45、收者尽管在R F C中没有指明,但大多数的系统在某一时刻只允许一个程序端点与某个本地 I P地址及U D P端口号相关联。当目的地为该 I P地址及端口号的U D P数据报到达主机时,就复制一份传给该端点。端点的I P地址可以含星号,正如我们前面讨论的那样。例如,在SunOS 4.1.3中,我们启动一个端口号为9 9 9 9的服务器,本地I P地址含有星号:sun % sock -u -s 9999接着,如果启动另一个具有相同本地地址和端口号的服务器,那么它将不运行,尽管我们指定了- A选项:sun % sock -u -s 9999 我们预计它会失败cant bind local addre

46、ss: Address already in usesun % sock -u -s -A 9999 因此,这次尝试- A参数cant bind local address: Address already in use在一个支持多播的系统上(第 1 2章) ,这种情况将发生变化。多个端点可以使用同一个 I P地址和 U D P端口号,尽管应用程序通常必须告诉 A P I是可行的(如,用 - A标志来指明S O _ R E U S E A D D Rs o c k e t选项) 。4 . 4 B S D支持多播传送,需要应用程序设置一个不同的s o c k e t选项(S O _ R E U

47、S E P O R T)以允许多个端点共享同一个端口。另外,每个端点必须指定这个选项,包括使用该端口的第一个端点。当U D P数据报到达的目的I P地址为广播地址或多播地址,而且在目的 I P地址和端口号处有多个端点时,就向每个端点传送一份数据报的复制(端点的本地 I P地址可以含有星号,它可匹配任何目的I P地址) 。但是,如果U D P数据报到达的是一个单播地址,那么只向其中一个端点传送一份数据报的复制。选择哪个端点传送数据取决于各个不同的系统实现。广播和多播在第1章中我们提到有三种I P地址:单播地址、广播地址和多播地址。本章将更详细地介绍广播和多播。广播和多播仅应用于 U D P,它们

48、对需将报文同时传往多个接收者的应用来说十分重要。T C P是一个面向连接的协议,它意味着分别运行于两主机(由 I P地址确定)内的两进程(由端口号确定)间存在一条连接。考虑包含多个主机的共享信道网络如以太网。每个以太网帧包含源主机和目的主机的以太网地址(4 8 b i t) 。通常每个以太网帧仅发往单个目的主机,目的地址指明单个接收接口,因而称为单播( u n i c a s t )。在这种方式下,任意两个主机的通信不会干扰网内其他主机(可能引起争夺共享信道的情况除外) 。考虑包含多个主机的共享信道网络如以太网。每个以太网帧包含源主机和目的主机的以太网地址(4 8 b i t) 。通常每个以太

49、网帧仅发往单个目的主机,目的地址指明单个接收接口,因而称为单播( u n i c a s t )。在这种方式下,任意两个主机的通信不会干扰网内其他主机(可能引起争夺共享信道的情况除外) 。为了弄清广播和多播,需要了解主机对由信道传送过来帧的过滤过程。图1 2 - 1说明了这一过程。首先,网卡查看由信道传送过来的帧,确定是否接收该帧,若接收后就将它传往设备驱动程序。通常网卡仅接收那些目的地址为网卡物理地址或广播地址的帧。另外,多数接口均被设置为混合模式,这种模式能接收每个帧的一个复制。作为一个例子,t c p d u m p使用这种模式。目前,大多数的网卡经过配置都能接收目的地址为多播地址或某些

50、子网多播地址的帧。对于以太网,当地址中最高字节的最低位设置为 1时表示该地址是一个多播地址,用十六进制可表示为 0 1 : 0 0 : 0 0 : 0 0 : 0 0 : 0 0(以太网广播地址ff : ff : ff : ff : ff : ff可看作是以太网多播地址的特例) 。如果网卡收到一个帧,这个帧将被传送给设备驱动程序(如果帧检验和错,网卡将丢弃该帧) 。设备驱动程序将进行另外的帧过滤。首先,帧类型中必须指定要使用的协议( I P、A R P等等) 。其次,进行多播过滤来检测该主机是否属于多播地址说明的多播组。设备驱动程序随后将数据帧传送给下一层,比如,当帧类型指定为 I P数据报时

51、,就传往I P层。I P根据I P地址中的源地址和目的地址进行更多的过滤检测。如果正常,就将数据报传送给下一层(如T C P或U D P) 。每次U D P收到由I P传送来的数据报,就根据目的端口号,有时还有源端口号进行数据报过滤。如果当前没有进程使用该目的端口号,就丢弃该数据报并产生一个 I C M P不可达报文(T C P根据它的端口号作相似的过滤) 。如果U D P数据报存在检验和错,将被丢弃。使用广播的问题在于它增加了对广播数据不感兴趣主机的处理负荷。拿一个使用 U D P广播应用作为例子。如果网内有 5 0个主机,但仅有2 0个参与该应用,每次这 2 0个主机中的一个发送U D P

52、广播数据时,其余 3 0个主机不得不处理这些广播数据报。一直到 U D P层,收到的U D P广播数据报才会被丢弃。这 3 0个主机丢弃U D P广播数据报是因为这些主机没有使用这个目的端口。 受限的广播受限的广播地址是 2 5 5 . 2 5 5 . 2 5 5 . 2 5 5。该地址用于主机配置过程中 I P数据报的目的地址,此时,主机可能还不知道它所在网络的网络掩码,甚至连它的 I P地址也不知道。在任何情况下,路由器都不转发目的地址为受限的广播地址的数据报,这样的数据报仅出现在本地网络中。一个未解的问题是:如果一个主机是多接口的,当一个进程向本网广播地址发送数据报时,为实现广播,是否应

53、该将数据报发送到每个相连的接口上?如果不是这样,想对主机所有接口广播的应用必须确定主机中支持广播的所有接口,然后向每个接口发送一个数据报复制。大多数B S D系统将2 5 5 . 2 5 5 . 2 5 5 . 2 5 5看作是配置后第一个接口的广播地址,并且不提供向所属具备广播能力的接口传送数据报的功能。不过, r o u t e d(见1 0 . 3节)和r w h o d(B S Dr w h o客户的服务器)是向每个接口发送 U D P数据报的两个应用程序。这两个应用程序均用相似的启动过程来确定主机中的所有接口,并了解哪些接口具备广播能力。同时,将对应于那种接口的指向网络的广播地址作为

54、发往该接口的数据报的目的地址。指向网络的广播指向网络的广播地址是主机号为全1的地址。A类网络广播地址为n e t i d . 2 5 5 . 2 5 5 . 2 5 5,其中n e t i d为A类网络的网络号。一个路由器必须转发指向网络的广播,但它也必须有一个不进行转发的选择。 指向子网的广播指向子网的广播地址为主机号为全 1且有特定子网号的地址。作为子网直接广播地址的 I P地址需要了解子网的掩码。例如,如果路由器收到发往 1 2 8 . 1 . 2 . 2 5 5的数据报,当 B类网络1 2 8 . 1的子网掩码为2 5 5 . 2 5 5 . 2 5 5 . 0时,该地址就是指向子网的

55、广播地址;但如果该子网的掩码为2 5 5 . 2 5 5 . 2 5 4 . 0,该地址就不是指向子网的广播地址。 指向所有子网的广播指向所有子网的广播也需要了解目的网络的子网掩码,以便与指向网络的广播地址区分开。指向所有子网的广播地址的子网号及主机号为全 1。例如,如果目的子网掩码为2 5 5 . 2 5 5 . 2 5 5 . 0,那么I P地址1 2 8 . 1 . 2 5 5 . 2 5 5是一个指向所有子网的广播地址。然而,如果网络没有划分子网,这就是一个指向网络的广播。Almquist 1993 指出RFC 922要求将一个指向所有子网的广播传送给所有子网,但当前的路由器没有这么做

56、。这很幸运,因为一个因错误配置而没有子网掩码的主机会把它的本地广播传送到所有子网。例如,如果I P地址为1 2 8 . 1 . 2 . 3的主机没有设置子网掩码,它的广播地址在正常情况下的默认值是1 2 8 . 1 . 2 5 5 . 2 5 5。但如果子网掩码被设置为255.255.255.0,那么由错误配置的主机发出的广播将指向所有的子网。1 9 8 3年问世的4 . 2 B S D是第一个影响广泛的T C P / I P的实现,它使用主机号全0作为广播地址。一个最早提到广播IP地址的是IEN 212 Gurwitz and Hinden 1982,它提出用主机号中的1比特来表示I P广播

57、地址(IENs 是互联网试验注释,基本上是R F C的前身) 。RFC 894 Hornig 1984认为4.2BSD使用不标准的广播地址,但RFC 906 Finlayson 1984注意到对广播地址还没有I n t e r n e t标准。R F C编辑在RFC 906中加了一个脚注承认缺少标准的广播地址,并强烈推荐将主机号全1作为广播地址。尽管1 9 8 6年的4 . 3 B S D采用主机号全1表示广播地址,但直到9 0年代早期,操作系统(著名的是SunOS 4.x)还继续使用非标准的广播地址。广播的例子广播是怎样传送的?路由器及主机又如何处理广播?很遗憾,这是难以回答的问题,因为它依

58、赖于广播的类型、应用的类型、T C P / I P实现方法以及有关路由器的配置。首先,应用程序必须支持广播。如果执行sun % ping 255.255.255.255/usr/etc/ping: unknown host 255.255.255.255打算在本地电缆上进行广播。但它无法进行,原因在于该应用程序( p i n g)中存在一个程序设计上的问题。大多数应用程序收到点分十进制的 I P地址或主机名后,会调用函数i n e t _ a d d r( 3 )来把它们转化为32 bit的二进制I P地址。假定要转化的是一个主机名,如果转化失败,该库函数将返回- 1来表明存在某种差错(例如是

59、字符而不是数字或串中有小数点) 。但本网广播地址(2 5 5 . 2 5 5 . 2 5 5 . 2 5 5)也被当作存在差错而返回- 1。大多数程序均假定接收到的字符串是主机名,然后查找D N S(第1 4章) ,失败后输出差错信息如“未知主机” 。如果我们修复p i n g程序中这个欠缺,结果也并不总是令人满意的。在 6个不同系统的测试中,仅有一个像预期的那样产生了一个本网广播数据报。大多数则在路由表中查找 I P地址2 5 5 . 2 5 5 . 2 5 5 . 2 5 5,而该地址被用作默认路由器地址,因此向默认路由器单播一个数据报。最终该数据报被丢弃。指向子网的广播是我们应该使用的。

60、在6 . 3节中,我们向测试网络(见扉页前图)中I P地址为140.252.13.63 的以太网发送数据报,并接收以太网中所有主机的应答。与子网广播地址关联的每个接口是用于命令i f c o n f i g(见3 . 8节)的值。如果我们p i n g那个地址,预期的结果是:I P通过目的地址(1 4 0 . 2 5 2 . 1 3 . 6 3)来确定,这是指向子网的广播地址,然后向链路层的广播地址发送该数据报。在6 . 3节提到的这种广播类型的接收对象为局域网中包括发送主机在内的所有主机,因此可以看到除了收到网内其他主机的答复外,还收到来自发送主机( s u n)的答复。在这个例子中,我们也

61、显示了执行 p i n g广播地址前后A R P缓存的内容。这可以显示广播与A R P之间的相互作用。执行 p i n g命令前A R P缓存是空的,而执行后是满的(也就是说,对网内其他每个响应回显请求的主机在 A R P缓存中均有一个条目) 。我们提到的该以太网数据帧被传送到链路层的广播地址( 0 x ffffffff)是如何发生的呢?由 s u n主机发送的数据帧不需要A R P。如果使用t c p d u m p来观察p i n g的执行过程,可以看到广播数据帧的接收者在发送它的响应之前,首先产生一个对 s u n主机的A R P请求,因为它的应答是单播的。在 4 . 5节我们介绍了一个

62、A R P请求的接收者(该例中是s u n)通常在发送A R P应答外,还将请求主机的I P地址和物理地址加入到A R P缓存中去。这基于这样一个假定:如果请求者向我们发送一个数据报,我们也很可能想向它发回什么。我们使用的p i n g程序有些特殊,原因在于它使用的编程接口(在大多数 U n i x实现中是低级插口(raw socket))通常允许向一个广播地址发送数据报。如果使用不支持广播的应用如T F T P,情况又如何呢?(T F T P将在第1 5章详细介绍。 )bsdi % t f t p 启动客户程序tftp connect 140.252.13.63 说明服务器的I P地址tft

63、p get temp.foo 试图从服务器或获取一个文件tftp: sendto: Permission deniedtftp q u i t 终止客户程序在这个例子中,程序立即产生了一个差错,但不向网络发送任何信息。产生这一切的原因在于,插口提供的应用程序接口A P I只有在进程明确打算进行广播时才允许它向广播地址发送U D P数据报。这主要是为了防止用户错误地采用了广播地址(正如此例)而应用程序却不打算广播。在广播U D P数据报之前,使用插口中A P I的应用程序必须设置S O _ B R O A D C A S T插口选项。并非所有系统均强制使用这个限制。某些系统中无需进程进行这个说明

64、就能广播UDP数据报。而某些系统则有更多的限制,需要有超级用户权限的进程才能广播。下一个问题是是否转发广播数据。有些系统内核和路由器有一选项来控制允许或禁止这一特性(见附录E) 。如果让路由器b s d i能够转发广播数据,然后在主机 s l i p上运行p i n g程序,就能够观察到由路由器b s d i转发的子网广播数据报。转发广播数据报意味着路由器接收广播数据,确定该目的地址是对哪个接口的广播,然后用链路层广播向对应的网络转发数据报。我们观察到它的确正常工作了,同时也看到 B S D系统中的p i n g程序检查重复的数据报序列号。如果出现重复序列号的数据报就显示 D U P !,这意

65、味着一个数据报已经在某处重复了,然而它正是我们所期望看到的,因为我们正向一个广播地址发送数据。我们还可以从远离广播所指向的网络上 的主机上来进行这个试验。在主机a n g o g h . c x . b e r k e l e y . e d u(和我们的网络距离1 4跳)上运行p i n g程序,如果路由器s u n被设置为能够转发所指向的广播,它还能正常工作。在这种情况下,这个 I P数据报(传送I C M P回显请求)被路径上的每个路由器像正常的数据报一样转发,它们均不知道传送的实际上是广播数据。接着最后一个路由器n e t b看到主机号为6 3,就将其转发给路由器s u n。路由器s

66、u n觉察到该目的I P地址事实上是一个相连子网接口上的广播地址,就将该数据报以链路层广播传往相应网络。多播I P多播提供两类服务:1) 向多个目的地址传送数据。有许多向多个接收者传送信息的应用:例如交互式会议系统和向多个接收者分发邮件或新闻。如果不采用多播,目前这些应用大多采用 T C P来完成(向每个目的地址传送一个单独的数据复制) 。然而,即使使用多播,某些应用可能继续采用T C P来保证它的可靠性。2) 客户对服务器的请求。例如,无盘工作站需要确定启动引导服务器。目前,这项服务是通过广播来提供的(正如第1 6章的B O O T P) ,但是使用多播可降低不提供这项服务主机的负担。 多播

67、组地址不像图1 - 5所示的其他三类I P地址(A、B和C) ,分配的28 bit均用作多播组号而不再表示其他。多播组地址包括为111 0的最高4 bit和多播组号。它们通常可表示为点分十进制数,范围从2 2 4 . 0 . 0 . 0到2 3 9 . 2 5 5 . 2 5 5 . 2 5 5。能够接收发往一个特定多播组地址数据的主机集合称为主机组 (host group)。一个主机组可跨越多个网络。主机组中成员可随时加入或离开主机组。主机组中对主机的数量没有限制,同时不属于某一主机组的主机可以向该组发送信息。一些多播组地址被I A N A确定为知名地址。它们也被当作永久主机组,这和 T C

68、 P及U D P中的熟知端口相似。同样,这些知名多播地址在 R F C最新分配数字中列出。注意这些多播地址所代表的组是永久组,而它们的组成员却不是永久的。例如,2 2 4 . 0 . 0 . 1代表“该子网内的所有系统组” ,2 2 4 . 0 . 0 . 2代表“该子网内的所有路由器组” 。多播地址2 2 4 . 0 . 1 . 1用作网络时间协议N T P,2 2 4 . 0 . 0 . 9用作R I P - 2 (见1 0 . 5节),2 2 4 . 0 . 1 . 2用作S G I公司的d o g f i g h t应用。多播组地址到以太网地址的转换I A N A拥有一个以太网地址块,

69、即高位 24 bit为0 0 : 0 0 : 5 e(十六进制表示) ,这意味着该地址块所拥有的地址范围从0 0 : 0 0 : 5 e : 0 0 : 0 0 : 0 0到0 0 : 0 0 : 5 e : ff : ff : ff。I A N A将其中的一半分配为多播地址。为了指明一个多播地址,任何一个以太网地址的首字节必须是 0 1,这意味着与I P多播相对应的以太网地址范围从0 1 : 0 0 : 5 e : 0 0 : 0 0 : 0 0到0 1 : 0 0 : 5 e : 7 f : ff : ff。这种地址分配将使以太网多播地址中的 2 3 b i t与I P多播组号对应起来,通

70、过将多播组号中的低位2 3 b i t映射到以太网地址中的低位2 3 b i t实现,这个过程如图1 2 - 3所示。由于多播组号中的最高 5 bit在映射过程中被忽略,因此每个以太网多播地址对应的多播组是不唯一的。 3 2个不同的多播组号被映射为一个以太网地址。例如,多播地址2 2 4 . 1 2 8 . 6 4 . 3 2(十六进制e 0 . 8 0 . 4 0 . 2 0)和2 2 4 . 0 . 6 4 . 3 2(十六进制e 0 . 0 0 . 4 0 . 2 0)都映射为同一以太网地址0 1 : 0 0 : 5 e : 0 0 : 4 0 : 2 0。既然地址映射是不唯一的,那么设

71、备驱动程序或 I P层(见图1 2 - 1)就必须对数据报进行过滤。因为网卡可能接收到主机不想接收的多播数据帧。另外,如果网卡不提供足够的多播数据帧过滤功能,设备驱动程序就必须接收所有多播数据帧,然后对它们进行过滤。局域网网卡趋向两种处理类型:一种是网卡根据对多播地址的散列值实行多播过滤,这意味仍会接收到不想接收的多播数据;另一种是网卡只接收一些固定数目的多播地址,这意味着当主机想接收超过网卡预先支持多播地址以外的多播地址时,必须将网卡设置为“多播混杂(multicast promiscuous)”模式。因此,这两种类型的网卡仍需要设备驱动程序检查收到的帧是否真是主机所需要的。即使网卡实现了完

72、美的多播过滤(基于48 bit的硬件地址) ,由于从D类I P地址到48 bit的硬件地址的映射不是一对一的,过滤过程仍是必要的。尽管存在地址映射不完美和需要硬件过滤的不足,多播仍然比广播好。单个物理网络的多播是简单的。多播进程将目的 I P地址指明为多播地址,设备驱动程序将它转换为相应的以太网地址,然后把数据发送出去。这些接收进程必须通知它们的 I P层,它们想接收的发往给定多播地址的数据报,并且设备驱动程序必须能够接收这些多播帧。这个过程就是“加入一个多播组” (使用“接收进程”复数形式的原因在于对一确定的多播信息,在同一主机或多个主机上存在多个接收者,这也是为什么要首先使用多播的原因) 。当一个主机收到多播数据报时,它必须向属于那个多播组的每个进程均传送一个复制。这和单个进程收到单播U D P数据报的U D P不同。使用多播,一个主机上可能存在多个属于同一多播组的进程。11.1 在11 . 5节中,向U D P数据报中写入1 4 7 3字节用户数据时导致以太网数据报片的发生。在采用以太网IEEE 802封装格式时,导致分片的最小用户数据长度为多少?11.3 假定有一个以太网和一份8 1 9 2字节的U D P数据报,那么需要分成多少个数据报片,每个数据报片的偏移和长度为多少?

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 资格认证/考试 > 自考

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号