工作进行中:我仍然需要清理这个&;添加完整的源代码。一个或多或少已经完成的版本的预计到达时间即将到来(TM)。最近更新时间:2020/09/10因为这件事怎么会出现在黑客新闻的头版?:D建议欢迎:如果有什么不清楚或错误的地方可以投诉!
这是路由器确切运行方式的内部视图。只有当您深入了解路由器实施情况时,才需要知道这一点。如果是这样的话,我表示哀悼。
在这个展览的最后,我将给你一个功能路由器的完整源代码(用P4写的,新的闪亮的软件定义的联网的东西)。我的目标是让你明白其中的每一句话。
我在下面的解释中附带了一些P4代码。我认为即使你从未看过P4,阅读它也是有用的,因为它比正文显示了更多的细节,而且我相信它有足够的伪代码。以下是您阅读它所需了解的内容摘要:
STANDARD_METADATA是告诉交换机如何处理数据包(如在特定端口上发送)。
确定应该如何处理数据包由控制平面/在慢速路径中/在CPU上/由控制器或类似短语完成。我将把所有这一切称为“控制面”。
实际转发分组是由数据平面/在快速路径/在硬件/在交换机等中完成的。我将把它称为“数据平面”。
交换机(或第2层交换机:-)是仅限第2层的1件事。它知道MAC地址和端口2等L2信息,但不知道IP地址等任何信息。它有一个MAC表:它将MAC地址映射到端口。
路由器(或某些人的词汇中的L3交换机)仅在L3上运行。它了解IP地址、接口和主机等L3信息。它不知道MAC地址或端口等L2内容。3事实上,如果您决定在L2上使用以太网以外的其他设备,则路由器的路由部分根本不需要更改。它有一个路由表(稍后详细说明):一个子网/前缀以及如何到达它们的表。
您通常所说的路由器(坐在那边的那个盒子)实际上是一个路由器(用于处理L3)和一个或多个交换机(用于处理L2),以及它们之间的胶水。它们实际上可能是硬件中的独立芯片。
你需要胶水把L2和L3粘合在一起。这种“L2.5”胶是ARP(或IPv6的NDP)。它通常位于路由器中,但它是胶水,而不是路由,您可以单独考虑。
当数据包到达并需要进一步发送时,必须对其执行以下操作:
它需要被路由:路由器根据L3信息决定它需要到达的目的地,用L3的话说-它将决定将其发送到哪台主机,但不会决定如何发送。这与路由表相对应。
它需要向下传递到L2:这是L2.5 ARP/NDP胶水将L3语言IP地址转换为L2语言MAC地址的地方。这是ARP表。
它需要在正确的端口上转发:交换机将数据包放在正确的端口上。这是MAC表。
该数据包具有目的IP地址。在路由表中使用最长前缀匹配(LPM)进行匹配,即它与IP地址前缀匹配。它可能是路由器直接连接到的主机(在某个接口上),也可能需要通过网关(通过某个接口)进一步发送。因此:路由表通过网关和接口将前缀映射到下一跳,或者通过接口将直接连接映射到下一跳。
请注意,下一跳的IP地址只在路由器的内存中:它在任何时候都不会出现在数据包中。
Action ipv4_through_ateway(ipv4_addr_t网关,interface_t iface){meta.out_interface=iface;meta.ipv4_next_hop=ateway;//通过网关发送}action ipv4_direct(Interface_T Iface){meta.out_interface=iface;meta.ipv4_next_hop=hdr.ipv4.dst_addr;//直接发送到目标}表IPv4_Routing{key={hdr.ipv4.dst_addr。//IPv4_Through_Gateway(Gateway,iFace)IPv4_DIRECT;//IPv4_DIRECT(IFACE)DROP;}DEFAULT_ACTION=DROP();//如果没有路由,则丢弃它--实际上,我们可能希望//向主机发送ICMP";无路由数据包。//请注意,这是默认路由,因此控制平面可能//希望在此处设置默认网关,而不是丢弃。Size=ROUTING_TABLE_SIZE;}。
如果我们没有因为没有路由而丢弃数据包,那么我们现在就知道了下一跳的IP地址和接口。(请注意,这是一台直接连接到我们的主机-它位于同一条线路上。)我们需要将其转换为L2 MAC地址,以便将其传递给交换机。我们通过ARP表执行此操作:
注意:接口在概念上属于那里,但IPv4地址应该是唯一的。我们无论如何都需要将接口存储在控制平面中,因为我们希望在条目即将到期时抢先重新发送ARP请求,但在数据平面中这并不是严格必要的。
这里出现了一个有趣的问题:如果没有匹配,即我们不知道IP的MAC地址,我们该怎么办?首先,我们发送ARP请求。然后,大多数路由器丢弃数据包(要么依靠重新传输,要么“没有人会错过它”)。存储数据包直到ARP回复返回(或直到其过期)也是可行的,但通常不会这样做。发送ARP请求通常在控制平面完成,因为需要限制ARP请求并使其过期等等。
Action set_dst_mac(Mac_Addr_T Dst_Addr){hdr.ethernet.dst_addr=dst_addr;}表IPv4_arp{key={meta.ipv4_next_hop:exact;//next_hop是我们在路由步骤中找到的主机;我们希望发送到//meta.out_interface:exact;//从概念上讲,这属于这里,但实际上next_hop应该是唯一的,所以//我们可以省略它}次操作。}DEFAULT_ACTION=DROP();SIZE=arp_table_size;}。
这是L2/交换机。它分别在每个接口上工作(它可以是硬件中的多个芯片)。它会得到一个带有某个目的MAC地址的数据包,并决定应该将其放入哪个端口。它使用MAC表来执行此操作:
//注意:我们正在对metadata.out_interfaceaction set_out_port(port_t端口){Standard_metadata.egress_spec=ports;}操作广播(){//实施取决于交换机。//在v1model中,使用与metadata.out_interface上的所有端口对应的组播组。}//我们称其为dmac--参见下面的whytable dmac{key={hdr.ethernet.dst_addr:exact;}action={set_out_port;//set_out_port(Port)Broadcast;//no params,use metadata.out_interface//记住在控制平面丢弃中设置0xffffffffffffffffff的广播;}
注意:实际交换机要比这复杂一些:例如,冗余链路意味着一个MAC地址可能位于多个端口上。但是,当您需要考虑这一点时,您会注意到这一点。通常,考虑简单版本就足够了。
Apply{routing.ply();//填写metadata.next_hop arp.ply();//将pkt.ethernet.dst_addr设置为next_hop dmac.ply()的MAC;//在pkt.ethernet.dst_addr的端口上发送。
(注意:虽然这在概念上是正确的,但实际上我们还希望应用下面提到的辅助表。完整的代码包含这些内容。)。
在您的家庭路由器中,它可能只有两个条目:内部接口上的本地网络(类似于192.168.0.0/24)=>;,以及通过外部接口上的ISP网关的默认路由。在这种情况下,路由表是静态的,由固件根据设置填写。
在小型公司路由器中,可能存在直接网络(如10.0.0.0/24)、位于10.0.1.0/24的远程办公室(通过VPN服务器)以及来自ISP的默认路由。默认路由和直接路由也将从设置中填写(由其操作系统填写),到远程办公室的路由将由VPN软件添加。
在ISP的路由器中,可能有一些路由用于连接到其他ISP,但没有默认路由。在这种情况下,控制平面将保留包含额外信息(如开销或多条路径)的路由表(路由信息库或RIB)的扩展版本,并从中计算路由表。RIB将使用某些控制平面协议(例如BGP)来填充。
在软件定义的网络练习中,它将由控制器脚本填充,该脚本从模拟器读取网络拓扑并对其执行合理的操作:-)。
我们需要使用ARP协议填充此地址。当我们试图查找IP地址但它不在我们的ARP表中时,我们会发送一个ARP请求,并在收到回复时添加该条目。条目过期(超时通常是几分钟到几小时)。
ARP请求通常由控制平面处理,因为限制它们是一个好主意(否则发送到不存在的主机将是一种简单的DOS攻击)。
(在我们的代码中,我们从数据平面发送ARP请求,因为控制器可能位于不同的物理网络上。我们也不节制任何东西,因为我们很懒。在生产中不要这样做!)。
这是由交换机观察流量来填写的:如果某个端口上出现数据包,则该数据包的源MAC地址与该端口相关联。
具体来说,我们要做的是:如果我们看到具有以前未见过的源MAC地址的数据包,我们会将其添加到MAC表中。当使用P4实现这一点时,最简单的选择是有一个包含SEW地址的附加表,如果我们在该表中遗漏了地址,则学习MAC。代码为:
Action mac_Learning(){//将pkt.ethernet.src_addr和Standard_metadata.ingress_port发送到控制器,//以便将其添加到DMAC表中。//(P4/硬件限制:我们不能直接这样做,因为表通常//只能由控制平面修改。原则上我们可以。)//控制平面还必须使用NoAction将其添加到SMAC。}//用于了解我们以前是否见过此源MAC,如果没有则获取它。//技巧:我们将default_action设置为mac_Learning,并且始终使用NoAction添加条目。//这样,我们只在miss.table SMAC{key={hdr.ethernet.src_addr:Exact;}Actions={Mac_Learning;NoAction;}default_action=。}//并且在Apply块中:Apply{...。Smac.ply();...}。
TODO链接到回购-我承诺现在任何十年都会清理它:D。
想知道比这个概述更多的信息吗?阅读TCP/IP插图,第1卷和第2卷!我是通过和一位经常读那本书的人交谈而得出这个结论的,所以这本书一定很棒。
1:这里的“端口”是指电缆所在的物理连接器,而不是“80”之类的TCP/UDP端口。如果这是一个惊喜,请参阅脚注2:-)
他说:如果你来自FreeBSD,你可能会尖叫说路由表有时知道MAC地址。这是一条通过避免额外查找来加快速度的捷径,但从概念上讲,它不应该存在。如果您碰巧实现了一个不关心性能的路由器,那么您不必这样做(我也没有这样做)。