子网前缀影响直连路由,最终路径由路由表选择
IPv4 前缀长度表示地址中有多少前导位标识网络。对于 192.0.2.5/24,网段是 192.0.2.0/24,普通广播地址是 192.0.2.255。配置接口通常会建立相应直连路由,但前缀不是完整路由算法。内核还要依据路由策略与路由表,为具体目的地选择路径,不能只看地址是否位于某个网段就推断全部行为。 坏掉的样子 前缀太短,会让主机把原本远端的目的地当成直接相连。保留地址 192.0.2.5,却把 /24 改成 /16,就把 192.0.3.9 纳入直连范围。在真实以太网中,这种错误可能让主机尝试解析一个根本不在当前链路上的邻居。于是地址看起来正确,下一跳决策却已经改变;反复核对地址字符串本身无法发现问题。 “不在自己的子网就走默认网关”也不完整。更具体的路由可以选择其他网关或接口;在已经选择的路由表内,更长的匹配前缀优先。明确的主机路由甚至可以覆盖本来位于接口子网内的目的地。应检查实际查询结果,而不是仅凭接口地址重新猜测内核会怎么做。默认路由只处理没有更具体匹配的相应流量。 地址数量计算也需要上下文。普通 /24 的网络地址与定向广播地址通常不用于主机分配,但“减二”不是统一公式。RFC 3021 允许点到点 /31 链路的两个地址都用作端点,/32 则标识单个地址。这些规则解释地址语义,没有证明配置中的两端能够通过当前物理拓扑交换数据。可用地址集合和实际通信仍须区分。 快速验证 将脚本保存为 check-subnet.py,准备 Docker 后执行 python3 check-subnet.py。程序给固定 Python 镜像安装 iproute2 6.1.0,再只向一次性容器授予 NET_ADMIN;容器没有外部网络接入。脚本创建虚拟 dummy 接口并修改该容器的路由,不改变宿主接口、路由或生产网关。构建下载与运行时路由实验也处于不同阶段: from pathlib import Path from tempfile import TemporaryDirectory import json,subprocess,uuid recipe='''FROM python@sha256:528257d48c1da0dcecc2e725d1ae34498d60c965f1241e39cd6a85a8859bdf84 RUN apt-get update && apt-get install -y --no-install-recommends iproute2=6.1.0-3 && rm -rf /var/lib/apt/lists/* ''' program=r''' from ipaddress import IPv4Address,IPv4Interface,IPv4Network import json,subprocess def ip(*args): result=subprocess.run(['ip',*args],capture_output=True,text=True,timeout=3) assert result.returncode==0,result.stderr return result.stdout def route(address):return json.loads(ip('-j','route','get',address))[0] interface=IPv4Interface('192.0.2.5/24');network=interface.network assert str(network.network_address)=='192.0.2.0' and str(network.broadcast_address)=='192.0.2.255' assert IPv4Address('192.0.2.77') in network and IPv4Address('192.0.3.9') not in network try:IPv4Network('192.0.2.5/24');raise AssertionError('host bits accepted in strict network') except ValueError:strict_rejected=True point_to_point=[str(a) for a in IPv4Network('192.0.2.0/31').hosts()] assert point_to_point==['192.0.2.0','192.0.2.1'] ip('link','add','lab0','type','dummy');ip('link','set','lab0','up') ip('address','add','192.0.2.5/24','dev','lab0');ip('route','add','default','via','192.0.2.1','dev','lab0') ip('route','add','203.0.113.0/24','via','192.0.2.2','dev','lab0') direct=route('192.0.2.77');specific=route('203.0.113.9');default=route('198.51.100.9');before=route('192.0.3.9') assert direct['dev']=='lab0' and 'gateway' not in direct assert specific['gateway']=='192.0.2.2' and default['gateway']==before['gateway']=='192.0.2.1' ip('route','add','192.0.2.77/32','via','192.0.2.1','dev','lab0');overridden=route('192.0.2.77') assert overridden['gateway']=='192.0.2.1' ip('address','del','192.0.2.5/24','dev','lab0');ip('address','add','192.0.2.5/16','dev','lab0') ip('route','replace','default','via','192.0.2.1','dev','lab0');wrong=route('192.0.3.9') assert wrong['dev']=='lab0' and 'gateway' not in wrong print(json.dumps({'interface':str(interface),'network':str(network),'broadcast':str(network.broadcast_address), 'strict_host_bits_rejected':strict_rejected,'point_to_point_31_hosts':point_to_point, 'connected':direct,'more_specific_route':specific,'default':default,'host_route_overrides_subnet':overridden, 'destination_before_widening':before,'destination_after_wrong_16':wrong,'iproute_version':ip('-V').strip()})) ''' name='xbcatm-subnet-'+uuid.uuid4().hex[:10] def docker(*args,**kwargs):return subprocess.run(['docker',*args],capture_output=True,text=True,timeout=300,**kwargs) try: with TemporaryDirectory(prefix=name+'-') as directory: root=Path(directory);(root/'Dockerfile').write_text(recipe) build=docker('build','-t',name,str(root));assert build.returncode==0,build.stderr result=docker('run','--rm','-i','--name',name,'--network','none','--cap-add','NET_ADMIN', '--memory','128m','--pids-limit','64',name,'python3','-',input=program) assert result.returncode==0,result.stderr print(json.dumps(json.loads(result.stdout),sort_keys=True)) finally: docker('rm','-f',name);docker('image','rm',name) Python 区分接口 192.0.2.5/24 与其网段 192.0.2.0/24。严格网络解析拒绝前者,因为主机位非零;/31 示例则列出 192.0.2.0 和 192.0.2.1 两个可用端点。这个计算确认地址集合,实验没有配置可工作的点到点链路,也没有测试这两个地址之间的转发。不能把库返回两个值,写成真实链路已经验证成功。 ...