资讯详情

资讯详情

IP编址与转发:子网掩码、CIDR与最长前缀匹配实战

网际协议里真正容易卡住人的往往不是地址格式而是转发和编址这两件事怎么咬合在一起。我见过不少人能把子网掩码算得飞快却在目的地址和网关不在同一网段这种问题面前愣半天也有人能背出最长前缀匹配的定义但让他解释为什么一条更具体的路由会抢走本该走默认路由的流量就说不太清楚了。编址决定地址空间怎么切分、谁属于谁、一个地址块里哪些能用哪些不能用转发决定一个已经到达路由器的包下一步该从哪个口出去、交给谁。把这两件事分开看再合起来看很多模糊的地方会突然变得清楚。这篇按我自己的理解顺序来先把IP层的职责边界划清楚再拆地址结构为什么长成这样然后讲转发表到底怎么被查最后落到子网规划的手算过程和几条能直接敲的命令上。中间会穿插一些实际踩过的坑尤其是那些文档里不太写、但一上手就撞上的细节。1. 把转发和路由分开IP层的职责边界才清楚1.1 无连接、尽力而为IP层承诺了什么又拒绝承诺什么IP协议在设计上做了一个非常克制的选择它只负责把数据报从源主机送到目的主机而且只承诺尽力。不保证送达不保证按序不保证不重复也不保证时延。这个选择在当时看是妥协放到今天看反而是它活下来的原因——网络层不背负可靠性可靠性被推到端系统用传输层去解决中间设备就可以做得足够简单、足够快。这个简单体现在哪路由器收到一个包看目的地址、查表、转发出去它不关心这个包属于哪个连接不保存任何按连接维度的状态。这也是为什么IP层被叫做无连接每个数据报都是独立处理的前后两个包可能走完全不同的路径后发的先到是常态而不是异常。理解这一点很关键因为它直接决定了很多现象的合理性。比如你抓包看到同一连接的两个包到达顺序反了那不是网络出错而是设计使然再比如路由器内存吃紧时丢包也不会通知源端我丢了只能靠上层超时重传。我在排查时见过有人把这类现象当成链路故障去查折腾半天才发现是自己对IP层语义的预期错了。1.2 控制平面写表数据平面查表路由和转发这两个词在日常交流里经常被混用但在实现层面它们是两件完全不同的事。路由是控制平面的活运行路由协议、收集拓扑信息、计算路径最终产出一张路由信息库。转发是数据平面的活每个到达的包都要在极短时间内完成一次查表动作决定出接口和下一跳。控制平面的更新频率是秒级甚至分钟级数据平面的查表频率是线速级别——一个万兆口满负荷时每秒要处理上千万个包。这两个数量级差了六七个数量级所以实际设备里一定是分开实现的控制平面算好的结果被编译成一张转发表下发给硬件硬件只管查不管算。转发表里的条目通常比路由信息库精简因为硬件表项是稀缺资源。我在调一个转发异常的问题时最有用的一个习惯就是先确认路由表里有和转发表里有是不是同一件事。有些平台上两者会短暂不一致配置刚下发时路由表已经能看到但硬件表还没刷新完这时候抓包看到的现象就是路由明明配了包却没出去。等几秒再测或者主动触发一次表项同步问题就消失了。这类问题不看实现分层是想不明白的。1.3 一个包从主机到主机的完整决策链把决策链从头走一遍转发和编址的关系就浮出来了。应用层把数据交给传输层加上端口号网络层封装IP头填上源地址和目的地址TTL设一个初始值常见64或128然后主机拿目的地址和自己配置的掩码做一次与运算判断目的主机是否在同一网段。如果在同一网段直接对目的地址做地址解析拿到下一跳的链路层地址如果不在同一网段就对网关地址做地址解析把包交给网关。包进入第一台路由器路由器查转发表找到出接口和下一跳把TTL减一重新封装链路层帧头源和目的链路层地址都换成本跳的转发出去。这个过程逐跳重复直到最后一台路由器发现目的网段直连直接投递。这里有个特别值得记住的对照IP地址端到端不变链路层地址逐跳改变。源IP和目的IP在整个旅程中基本不动地址转换是例外属于另一层机制但每一跳的帧头都是新的。很多人对转发的困惑就来自把这两层地址的角色搞混了以为路由器是照着目的MAC转发的。恰恰相反路由器看的是目的IPMAC只是把包送到下一跳的手段。2. IP地址的层次编址设计的取舍2.1 32位地址怎么拆成网络号与主机号IPv4地址是32位写成点分十进制只是为了人看着舒服本质是一个从0到4294967295的整数。这个整数被切成两段前一段标识网络后一段标识该网络内的主机。切在哪就是编址的核心问题。为什么要切因为路由表不能存全世界每一台主机的地址。如果地址是扁平的全球几十亿个可达地址就要几十亿条表项任何设备都扛不住。加上层次之后路由器只需要知道某个网段在哪个方向一个表项覆盖一大片地址表项数量就降到了可控范围。这是编址设计最根本的动机也是为什么子网划分、路由聚合这些概念存在——它们都是在维护这个层次结构。早期的做法是分类编址靠地址的前几位固定判断类别。A类第一位是0网络号8位B类前两位是10网络号16位C类前三位是110网络号24位D类前四位1110用于组播E类前四位1111保留。这个方案的好处是不需要额外信息就能判断网络号长度坏处是粒度太粗。2.2 分类编址为什么撑不下去分类编址的问题不是技术上错了而是和真实需求对不上。一个中等规模的组织需要几百到几千个地址A类给它一千六百万个浪费到离谱C类只给254个又不够用只有B类勉强合适结果所有人都去申请B类B类地址空间很快见底。更麻烦的是路由表膨胀。分类编址下一个组织即使只用了一个B类地址段里的一小部分对外也得宣告整个B类路由器没法把相邻的多个C类合并成一条因为类别边界是死的。地址空间消耗和路由表规模两个问题同时恶化这套方案就走到头了。我第一次认真理解这段历史是在算一个老网络的地址规划时发现的那个网络里到处是半个B类用了几十个地址的情况剩下的地址既不能给别人用也没法聚合白白占着。分类编址的遗产在今天的很多内网里还能看到痕迹比如习惯性地按A、B、C的边界去切地址段。2.3 子网掩码与CIDR把前缀长度变成一等公民子网掩码解决的是在类别内部再切一刀的问题。它是一个32位数网络位全1、主机位全0和地址做与运算就得到网络地址。比如掩码255.255.255.0前三段是网络最后一段是主机。这样即使拿到一个B类地址也可以按需切成多个/24的子网内部路由就精细多了。CIDR无类域间路由把这件事推到极致前缀长度可以是0到32之间的任意值不再受类别约束写法就是地址/长度。192.168.16.0/20表示前20位是网络位剩下12位是主机位能容纳4094个可用地址。这个表示法同时解决了编址和聚合两个问题——因为前缀长度自由相邻的地址块只要前缀能对齐就可以合并成一条更短前缀的路由。举个具体的聚合例子。四条路由分别是192.168.0.0/24、192.168.1.0/24、192.168.2.0/24、192.168.3.0/24。看第三段的二进制0是000000001是000000012是000000103是00000011只有最低两位在变高六位完全一致。所以这四块可以合成192.168.0.0/22一条表项覆盖四条。判断能不能聚合的方法就是这个把候选地址块写成二进制看从第几位开始出现分歧共同前缀就是聚合后的长度。提示聚合的前提是地址块连续且按边界对齐。192.168.0.0/24和192.168.2.0/24中间夹着1.0/24这两条就不能直接合成/22硬合会把不属于自己的地址也宣告出去属于典型的配置事故。3. 转发平面的核心动作最长前缀匹配3.1 转发表里到底有什么一条转发表项最少包含三样东西目的前缀、下一跳、出接口。目的前缀用地址/长度表示下一跳是邻接设备的地址直连路由可以为空出接口是本机哪个口发出去。有些实现还会带度量值、优先级、标签等信息但查表的核心逻辑只依赖前缀。表里通常有几类条目并存直连路由本机接口配置地址后自动产生前缀就是接口网段、静态路由手工配置、动态路由协议学来的、默认路由0.0.0.0/0前缀长度0能匹配任何地址。默认路由的存在是为了兜底当一个目的地址在表里找不到更具体的匹配时就按默认路由走。这里有个容易忽略的点默认路由不是特殊规则它只是一条前缀长度为0的普通表项。理解这一点后面讲匹配顺序时就顺了。3.2 最长前缀匹配的逐步演算规则只有一条在所有能匹配目的地址的表项里选前缀最长的那条。前缀越长覆盖范围越窄描述越精确。假设某台路由器的转发表是这样的目的前缀下一跳出接口0.0.0.0/010.0.0.1eth010.0.0.0/810.1.1.1eth110.1.0.0/1610.2.2.2eth210.1.2.0/2410.3.3.3eth310.1.2.128/25直连eth4现在来一个目的地址10.1.2.200。逐条看/0匹配任何地址都匹配、/8匹配、/16匹配、/24匹配前24位一致、/25也匹配10.1.2.128/25覆盖128到255200在里面。五条都命中选最长的/25从eth4直连发出。换成10.1.3.5/0匹配、/8匹配、/16匹配10.1开头/24不匹配第三段是3不是2/25也不匹配。选/16走eth2。再换203.0.113.7只有/0匹配走默认路由eth0。这个演算过程看起来简单但它是所有转发异常的第一怀疑对象。我遇到过好几次路由配了但流量还是走老路的情况最后查出来都是表里存在一条更长前缀的旧路由把流量截走了。排查时先用查表命令看实际命中的是哪条比猜快得多。3.3 默认路由、主机路由与等价多路径默认路由的实战价值在于收敛表规模。一个末梢网络只要一条默认路由指向出口内部所有未知目的地址都往出口送不需要知道全网明细。这就是所谓边缘默认、核心明细的常见布局。主机路由是另一个极端/32前缀只匹配单个地址。它的典型用途是给某台关键设备单独指定路径或者在做流量牵引时把某个地址的流量强行导到特定设备上。主机路由因为前缀最长永远优先于任何网段路由所以用起来很有力也很容易变成埋雷——配置的人走了后来的人死活想不通为什么这台机器不走正常路径。等价多路径ECMP出现在有多条同前缀、同优先级路由的情况下。设备会把这些下一跳都保留下来按流做哈希分流让不同连接走不同路径既提升带宽又保留冗余。这里要注意的是哈希的粒度多数实现按五元组哈希同一条连接的所有包走同一路径避免乱序但如果你做了地址转换哈希输入变了分流结果也会变。这一点在做链路扩容后验证流量分布时特别值得留意。硬件实现上高速转发设备通常用三态内容寻址存储器来做查表。它的特点是所有表项并行比较一次就能得出最长匹配结果代价是容量有限、功耗高。所以真实设备会把最常用的前缀放进硬件表长尾的放到软件路径处理这也是为什么有些目的地址的转发时延会明显高于其他地址。4. 子网划分实战从需求到地址块4.1 VLSM规划一个大块怎么切变长子网掩码的核心思想是不同大小的网段用不同的前缀长度按需分配减少浪费。规划顺序应该是从大到小先满足大块再用剩下的空隙填小块。拿一个具体需求走一遍。手上有一个10.10.0.0/16需要满足总部500台主机、分部200台、两个小部门各50台、四条点对点互联链路。第一步算每个网段需要的主机位。可用地址数是2的h次方减2去掉网络地址和广播地址。500台需要2^9512减2得510够用所以主机位9位前缀长度32-923。分部200台需要2^8256减2得254主机位8位前缀/24。50台需要2^664减2得62主机位6位前缀/26。点对点链路只需要2个地址理论上2^24减2得2前缀/30正合适。第二步按顺序分配用途网段可用范围可用数总部10.10.0.0/2310.10.0.1 - 10.10.1.254510分部10.10.2.0/2410.10.2.1 - 10.10.2.254254小部门A10.10.3.0/2610.10.3.1 - 10.10.3.6262小部门B10.10.3.64/2610.10.3.65 - 10.10.3.12662链路110.10.3.128/30.129 - .1302链路210.10.3.132/30.133 - .1342链路310.10.3.136/30.137 - .1382链路410.10.3.140/30.141 - .1422这里有个细节值得说/23的网段跨越了10.10.0.x和10.10.1.x两段网络地址是10.10.0.0广播地址是10.10.1.255。很多人算到这儿会下意识把广播地址写成10.10.0.255这是把/23当成两个/24了。判断方法很简单主机位是9位全1就是广播地址第三段和第四段一起看10.10.1.255的二进制后9位全是1没错。4.2 聚合让路由表瘦下来规划完之后如果这些网段都要对外宣告八条路由有点多。看看能不能聚合。前四块10.10.0.0/23、10.10.2.0/24、10.10.3.0/26、10.10.3.64/26合起来覆盖了10.10.0.0到10.10.3.127能不能压成一条把第三段写二进制0是000000001是000000012是000000103是00000011。共同前缀到前30位即第三段的高6位也就是说10.10.0.0/23和10.10.2.0/24合起来是10.10.0.0/22但10.10.3.x只覆盖了0到127128到255还在用四条/30链路所以不能整体合成/22否则会把链路段也包括进去。如果链路段的地址规划时挪到别的地方比如放到10.10.4.0/24里那么前四块就能干净地合成10.10.0.0/22。这就是规划时预留空隙的价值地址分配不只看当下够不够用还要考虑未来能不能聚合。我现在的习惯是给每类用途划一个连续的地址池池内再细分池与池之间留出对齐的边界后期聚合会舒服很多。反面的教训也见过有的网络地址分配是谁先申请谁先拿东一块西一块最后出口路由器上挂了上百条明细路由每次扩容都要手工加还容易漏。这种局面下想做聚合就得重新规划地址代价非常大。4.3 特殊地址清单与易错点编址里有几个地址段有固定含义配置时踩错会出很隐蔽的问题地址/前缀含义注意点0.0.0.0/8本网络只能作源地址不能作目的地址127.0.0.0/8环回整个/8都是环回不只是127.0.0.1169.254.0.0/16链路本地地址获取失败时自动配置不能跨网段10.0.0.0/8私有地址不出公网出口需地址转换172.16.0.0/12私有地址范围是172.16到172.31容易记错成172.16/16192.168.0.0/16私有地址家庭和小型网络最常见224.0.0.0/4组播不能当单播地址配在接口上240.0.0.0/4保留除255.255.255.255外不应出现几个高频错误。第一个是172.16.0.0/12的范围很多人记成172.16.0.0/16结果把172.17.x.x当成公网地址配出去了。第二个是环回段以为只有127.0.0.1是环回实际上127开头的整个/8都是随便配一个127.1.1.1也能通这在测试时很方便。第三个是每个子网的网络地址和广播地址不能用/24网段实际可用254个而不是256个规划时如果按256算最后两台机器会配不上地址。还有一个偏门的点点对点链路上用/31可以省地址两个地址都可用没有网络地址和广播地址的概念。这个做法在标准里有明确定义主流设备都支持。四条链路用/31的话只需要8个地址比/30省一半。不过老设备可能不认上线前要确认。5. 编址与转发在真实环境里的交互5.1 网关地址必须和主机同网段这不是建议是硬约束主机判断目的地址在不在本网段用的完全是自己的掩码。如果判断为不在它就去解析网关的链路层地址把包交给网关如果判断为在它直接解析目的地址根本不看网关。这就意味着网关地址必须落在主机自己的网段内否则主机的地址解析请求发出去没人应答包根本出不了网卡。这个约束在配置界面里通常不校验你填一个明显不在同网段的网关系统照样保存然后就是上不了网而且现象很迷惑——网关本身可能是通的因为从别的机器 ping 它能通。我遇到过一个典型案例某台服务器的地址是10.20.30.40/24网关填了10.20.31.1。从别的网段 ping 这台服务器能通因为路由是对的但从这台服务器往外 ping 全失败。查了半天路由表都对最后发现是网关不在同网段地址解析根本发不出去。改成10.20.30.1立刻就好。反向的问题也存在掩码配错导致同网段判断出现分歧。两台机器地址看着在同一个/24里但其中一台掩码写成了/25那么它对另一半地址的判断就是不同网段会去找网关而另一台认为同网段直接发地址解析。结果就是这两台机器互相 ping 不通但各自 ping 网关都正常。这种故障特别难查因为表面上看两台机器明明在一个网段。5.2 地址转换改变了什么没改变什么私有地址在出口做地址转换是极常见的做法它的作用是让内网地址复用、减少公网地址消耗。从转发角度看转换设备在转发路径上多做了一步把源地址换成出口地址同时记录映射关系回程包再按映射换回来。这件事对编址的影响是内网地址的规划可以完全独立于公网只要保证内部不冲突就行。这也是为什么私有地址段可以反复出现在不同组织内部互不影响。对转发的影响更值得注意转换设备必须看到双向流量才能维护映射所以它天然是个有状态的中间点。一旦映射表项超时被清掉回程包就找不到对应关系表现为连接卡住然后慢慢恢复。排查这类问题时看转换设备的会话表比看路由表有用得多。另外转换会改写地址某些把地址信息嵌在载荷里的协议就需要额外处理这也是这类协议在转换环境里容易出问题的原因。5.3 同一台设备上多个地址段时的转发细节一台路由器接口上配了主地址和从地址的情况很常见尤其是做地址迁移的过渡期。这时候要注意从地址所在的网段也算直连路由会进转发表。如果从地址的网段和别处冲突转发就会乱。迁移时的稳妥做法是先把新网段配成从地址验证连通性再把主机逐批切过去最后把主地址换成新网段、删掉旧的。整个过程两台网段的直连路由同时存在互不干扰。但如果中途有人在新网段上又配了静态路由指向别处直连路由和静态路由会打架——直连路由的优先级通常更高静态路由配了也不生效配置的人会以为没生效是设备问题。这类问题的通用排查思路是先确认实际命中的路由来源直连、静态还是动态再看优先级。不同来源的路由在表里是可以共存的最终生效的由优先级和前缀长度共同决定只看前缀长度是不够的。6. 排查与验证几条能直接用的命令和思路6.1 看地址、看路由、看实际命中Linux 环境下最常用的三条# 看接口地址和掩码 ip addr show # 看路由表 ip route show # 看某个目的地址实际会命中哪条路由 ip route get 203.0.113.10第三条是最有价值的。前两条是配置了什么第三条是实际会怎么走两者不一致的时候问题就藏在那儿。比如你明明配了走A口结果查出来走B口那说明表里有更具体的路由把流量截走了或者有策略路由干预。Windows 环境下对应的是route print和tracert前者看表后者看路径。tracert的价值在于它能告诉你包在哪一跳开始偏离预期。如果前三跳正常、第四跳突然跑到一个不该出现的方向那基本可以确定是中间某台设备的路由配错了。要注意tracert依赖回程的ICMP消息有些设备会限速或不回看到星号不一定代表断了可能只是不响应。6.2 分层验证的顺序排查连通性问题我习惯从下往上一层一层确认不要跳链路层接口是否 up有没有错误计数增长对端是否同一广播域。地址层本机地址和掩码是否正确网关是否在同网段内。地址解析能否解析到网关的链路层地址表项是否正常。路由层实际命中的路由是否符合预期下一跳是否可达。上层端口是否监听有没有策略拦截。这个顺序的理由是越往下越基础下层不通的话上层怎么配都没用。跳过下层直接查上层最常见的结局是花了半小时发现网线没插好。6.3 常见现象与根因对照现象优先怀疑验证方法能通网关访问远端失败网关未做转发、上游路由缺失、地址转换异常查网关的转发表和会话表网关都通不了掩码不一致、网关地址不在同网段、二层隔离对比两端掩码查地址解析表部分地址通、部分不通子网划分重叠或掩码分歧逐台核对地址和掩码路由表有但流量走错存在更长前缀的旧路由用查表命令看实际命中跨网段偶发丢包掩码不一致导致同网段判断分歧两端同时抓包看是否有地址解析风暴换设备后大面积不通新设备转发表未同步或默认路由缺失逐条比对新旧路由表这张表里最值得留意的是最后一行。设备替换是转发类故障的高发场景因为旧设备上那些配了但没人记得的路由不会自动跟着走。我的习惯是替换前把旧设备的完整路由表导出来和新设备逐条比对重点看默认路由、静态路由和那些指向具体主机的/32条目这三类最容易被遗漏。7. 一点个人经验编址和转发这两块单独看都不难难的是它们交织在一起的时候。我现在做地址规划会强迫自己多问一句这块地址以后能不能聚合而不是只看现在够不够分。多花十分钟把边界对齐后面能省掉很多次手工加路由的麻烦。排查方面有个习惯挺管用先确认实际命中再谈为什么。很多人一上来就看配置文件看完觉得没问题就卡住了。其实配置和实际行为之间还隔着表项下发、优先级比较、最长前缀匹配几道关直接看实际结果反而更快定位。命令敲下去三秒钟的事比盯着配置文件推演十分钟靠谱。最后一个容易忽略的点文档里写的可用地址数永远是理论值实际还要减掉网关占用的那个、可能的保留地址、以及某些设备会占用第一个可用地址做管理。规划时按理论值卡着上限分上线时一定会少一个。留点余量比事后改掩码省事得多。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →