尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

IPv6地址配置实战:从链路本地到全局路由,一文搞懂

IPv6地址配置实战:从链路本地到全局路由,一文搞懂 上个月帮朋友调试一台双网卡的服务器他跟我说“IPv6地址都配上去了可另一个网段的机器就是ping不通”。我登上去看了一眼地址确实在ip addr输出里一长串fe80::开头的地址看着挺正常但全局单播地址根本就没配到网卡上——严格说他配置的是一个“只在本链路有效的地址”。这其实是很多人配置IPv6时踩的第一个坑地址类型没分清配了也白配。IPv6地址配置这件事说难不难说简单也真不简单。难点不在于敲命令而在于理解你配的到底是什么。我做过很多次网络实验也踩过不少IPv6的坑总结下来就是只要把地址类型、作用范围和路由这三件事想清楚配置IPv6的整个逻辑就通了。这篇文章就用一个完整的实验带你从零把IPv6地址配置跑通一次。1. 先从一次“配了但ping不通”说起IPv6地址的类型和适用范围1.1 为什么IPv6地址“看起来配好了”却还是不通很多人第一次接触IPv6时都会遇到一个奇怪的现象用ip addr查看网卡明明有一长串地址看起来是配置成功了但ping对端就是不通。原因往往出在地址类型选错了。IPv6地址不像IPv4那样“一个IP就能全网用”它分为三种常见类型链路本地地址Link-Local Address以fe80::开头只在同一物理链路内有效路由器不会转发这个地址的报文。每块支持IPv6的网卡开机后会自动生成一个用于邻居发现、路由协议等底层通信。唯一本地地址Unique Local AddressULA以fc00::/7开头相当于IPv4里的私网地址只在企业内部网络使用公网不可路由。全局单播地址Global Unicast Address以2000::/3开头这是全球可路由的正式地址类似IPv4的公网地址也是跨网段通信时要用的地址。在Linux上只要网卡启用IPv6内核就会自动分配一个链路本地地址。所以当你打开ip addr时看到的一定会有fe80::开头的那条这只是“链路通了”并不能说明“全局配置成功”。我那位朋友就是只看到了fe80::的地址以为IPv6配置完了结果全局地址根本没配上另一网段的机器自然ping不通。这是配置IPv6时最容易被误导的一步。1.2 全局单播地址的结构网络前缀与接口标识符弄清了类型还要看懂全局单播地址的结构。一个全局IPv6地址可以分为两部分网络前缀Network Prefix前64位相当于IPv4里的网络号决定了这个地址属于哪个子网。接口标识符Interface Identifier后64位相当于IPv4里的主机号用于在同一子网内区分不同设备。用2001:db8:100::10/64这个地址为例斜杠后面的/64表示前64位2001:db8:100::是网络前缀。只要这个前缀相同的设备就在同一个二层网络里可以直接通过邻居发现协议通信前缀不同的设备就必须通过路由器转发。在实验环境里为了不干扰公网路由我习惯使用2001:db8::/32这个保留段。这个段是文档和教学用途的专用地址段不会被真实路由用来做实验非常安全。2. 搭建一个最小实验环境三台虚拟机管够2.1 实验拓扑设计思路配置IPv6不只需要一台机器因为单独的地址配置看不出问题。真正能验证配置是否正确的是两台设备之间的通信过程。所以我设计了一个最简单的跨网段拓扑PC1 (2001:db8:100::10/64) --- [eth0] R1 [eth1] --- PC2 (2001:db8:200::10/64)三台设备PC1连接“网段A”配置全局地址2001:db8:100::10/64默认网关指向R1。R1一台双网卡的Linux机器eth0连接网段A地址2001:db8:100::1/64eth1连接网段B地址2001:db8:200::1/64开启IPv6转发功能。PC2连接“网段B”配置全局地址2001:db8:200::10/64默认网关指向R1。这个拓扑虽然只有三台机器但覆盖了IPv6地址配置的全部核心知识点单机地址配置、同网段通信、跨网段路由、网关配置。跑通这个实验你对IPv6的理解会上一个台阶。2.2 虚拟化环境准备我用的是VMware Workstation你也可以用VirtualBox或者任何你熟悉的虚拟化平台。关键步骤在于虚拟网络的设置创建三个虚拟机操作系统建议使用Ubuntu Server或Rocky Linux命令行操作即可。在VMware的“虚拟网络编辑器”里自定义两个仅主机模式的网络例如VMnet1对应网段A和VMnet2对应网段B。PC1的网卡连接到VMnet1R1有两块网卡分别连接到VMnet1和VMnet2PC2的网卡连接到VMnet2。确保三台虚拟机的IPv6都处于启用状态。Linux系统默认是启用的不需要额外安装软件包。如果之前有人改过内核参数可以用sysctl net.ipv6.conf.all.disable_ipv6检查输出应为0。提示实验环境的地址一定不要用真实运营商分配的IPv6前缀使用2001:db8::/32文档保留段是最稳妥的做法。3. 键盘上的第一组配置在Linux上手动配置地址和路由3.1 用 ip 命令临时配置验证链路是否通畅实验环境准备好以后先把最基本的配置敲一遍。首选ip命令因为它立即生效适合在实验过程中反复调试# 在 R1 上执行 ip -6 addr add 2001:db8:100::1/64 dev eth0 ip -6 addr add 2001:db8:200::1/64 dev eth1 ip link set eth0 up ip link set eth1 up # 在 PC1 上执行 ip -6 addr add 2001:db8:100::10/64 dev eth0 ip link set eth0 up # 在 PC2 上执行 ip -6 addr add 2001:db8:200::10/64 dev eth0 ip link set eth0 up配置完成后先用ip -6 addr show检查一下结果。你会在每台机器上看到两类地址自动生成的fe80::链路本地地址以及手动添加的2001:db8::全局地址。下面先验证同网段通信。在PC1上ping R1的eth0地址ping -6 2001:db8:100::1这里第一个知识点就出来了第一次ping的时候大概率会卡一两秒才通甚至前几个包会超时。这不是配置错了而是IPv6邻居发现机制NDPNeighbor Discovery Protocol在起作用。PC1要发送报文给2001:db8:100::1但还不知道这个地址对应的MAC地址所以先发送邻居请求NS来获取对方的MAC地址收到邻居通告NA后才能完成通信。这就好比你在小区里要找一个门牌号先在楼下喊一嗓子问路。IPv4里有ARPIPv6里就是NDP。如果执行ip -6 neigh show你能看到刚刚通过NDP学习到的邻居表项包括PC1已经解析出来的R1地址。3.2 配置默认路由跨网段通信的前提同网段通了之后PC1 ping PC2仍然不通。这是正常的因为PC1的地址前缀是2001:db8:100::/64而目标地址2001:db8:200::10属于另一个前缀。PC1的路由表里没有到达这个前缀的条目就必须通过默认网关转发。此时需要给PC1和PC2添加默认路由# 在 PC1 上执行 ip -6 route add default via 2001:db8:100::1 dev eth0 # 在 PC2 上执行 ip -6 route add default via 2001:db8:200::1 dev eth0另一件关键的事情是在R1上开启IPv6转发# 在 R1 上执行 sysctl -w net.ipv6.conf.all.forwarding1 sysctl -w net.ipv6.conf.default.forwarding1为什么必须开启转发因为R1的两块网卡分布在两个不同网段默认情况下Linux不会把报文从一个接口转发到另一个接口这是出于安全考虑的默认策略。只有显式开启forwarding才允许R1作为路由器转发IPv6报文。开启转发后PC1再ping PC2的全局地址ping -6 2001:db8:200::10这次应该能通了。如果还是不通去R1上用tcpdump看一下报文有没有到达tcpdump -i eth1 icmp6 -n如果R1的eth1上能抓到echo request说明报文已经到了R1但没转发出去重点排查转发开关如果连R1都没到重点排查PC1的默认路由。3.3 配置永久化netplan与sysctl文件实验过程中用ip命令配置最方便但重启虚拟机后配置就丢了。要做成可复现的环境需要把配置固化到系统配置文件里。Ubuntu系统使用的是netplan配置文件的格式是YAML。编辑/etc/netplan/01-netcfg.yamlnetwork: version: 2 ethernets: eth0: addresses: - 2001:db8:100::1/64 routes: - to: default via: 2001:db8:100::1在PC1上via后面填R1在网段A的地址2001:db8:100::1在R1上eth0和eth1都需要配置地址但默认路由只需要配给R1自己。R1本身作为路由器不需要默认路由也完全可以工作——因为它的职责是转发而不是主动访问外网。netplan配置改完后执行sudo netplan apply再用ip -6 addr验证地址是否生效。R1的转发开关也要永久化编辑/etc/sysctl.confnet.ipv6.conf.all.forwarding 1 net.ipv6.conf.default.forwarding 1然后执行sysctl -p。注意有些系统上还需要在网卡配置里禁止接受路由通告accept_ra否则开启转发功能后网卡可能会错误地处理路由器通告报文影响自动配置行为。这个问题在动态获取场景下会很明显下文会详细说。4. 动态地址获取SLAAC与EUI-64背后的逻辑4.1 SLAACIPv6独有的自动配置机制手动配置地址只是开始真正体现IPv6设计思想的是它的无状态地址自动配置Stateless Address AutoconfigurationSLAAC。SLAAC的原理是网络里的路由器周期性地发送路由器通告RARouter Advertisement报文把本网段的前缀比如2001:db8:100::/64广播给所有设备。设备收到RA后自己生成一个接口标识符然后组合形成自己的全局地址。整个过程不需要DHCP服务器参与设备拿到前缀就能自己“算出”地址。这就是IPv6和IPv4一个巨大的区别IPv4如果不用DHCP就得人工配置私网IPIPv6即使没有DHCP设备也能通过网络前缀自动生成地址。路由器在这个过程里的角色就是“广播前缀”。为了让实验验证SLAAC可以把PC1的网卡配置改成自动获取sysctl -w net.ipv6.conf.eth0.accept_ra1 sysctl -w net.ipv6.conf.eth0.autoconf1然后重启网络服务或者直接删除手动配置的地址ip -6 addr flush dev eth0如果R1正常发送RA报文PC1会在几秒钟内自动生成一个2001:db8:100::前缀的地址。4.2 EUI-64算法从MAC地址到IPv6接口标识符SLAAC中的关键问题是设备如何“算出”自己的接口标识符最常用的算法叫EUI-64它会把网卡的MAC地址转换成一个64位的接口标识符。转换过程分三步把48位MAC地址从中间拆开插入fffe。翻转MAC地址的第7位U/L位。得到的后64位就是EUI-64接口标识符。举个具体的例子。假设PC1的MAC地址是00:0c:29:3a:2b:3c中间插入fffe后变成00:0c:29:ff:fe:3a:2b:3c翻转第一个字节00的第7位从0变成100变成02最终接口标识符是020c:29ff:fe3a:2b3c所以这个网卡自动生成的链路本地地址就是fe80::020c:29ff:fe3a:2b3c如果你在Linux上开启了SLAAC从RA得到前缀2001:db8:100::/64后生成的全局地址就是2001:db8:100::020c:29ff:fe3a:2b3c用命令查看SLAAC生成的地址ip -6 addr show dev eth0你会看到scope global dynamic这样的标记表示这是一个动态获取的全局地址。4.3 SLAAC和DHCPv6的分工合作有些人会问IPv6是不是不需要DHCP了其实不是。SLAAC只能分配地址但分配不了域名服务器DNS地址。如果一个网络还需要下发DNS配置就需要DHCPv6或者RA中携带的DNS选项RDNSS来配合。实际网络环境里SLAAC和DHCPv6常常是同时使用的功能SLAACDHCPv6有状态分配地址支持支持分配网关通过RA自动下发不支持分配DNS可以通过RA的RDNSS选项下发支持需要额外服务器不需要需要DHCPv6服务器在纯SLAAC环境里设备能自动拿到地址和网关但拿不到DNS在有状态DHCPv6环境里设备从DHCPv6服务器拿地址和DNS。理解这个区别排查动态IPv6问题时能少走很多弯路。5. 从命令行走到真实网络验证与排错的经验之谈5.1 配置完成后先看这三个状态做完一个实验最重要的是形成一套验证方法。我每次配置完IPv6地址都会习惯性地检查三样东西地址是否在网卡上ip -6 addr show dev eth0确认全局地址存在没有tentative或dadfailed状态。tentative表示地址还在重复地址检测DAD阶段dadfailed表示检测到冲突地址不可用。路由表是否正确ip -6 route show确认默认路由或明细路由存在。很多人地址配了但路由没配最后却去检查防火墙那是浪费时间。邻居表是否能解析到对方ip -6 neigh show确认对端MAC地址解析成功。如果邻居表里目标地址的状态是INCOMPLETE说明NDP请求发出去但没有回应多半是地址配置错了或者不在同一链路。这三步走完80%的IPv6通信问题都能定位出来。5.2 用 tcpdump 深度观察NDP交互过程命令输出的结果只能告诉你是怎么样的但看不到通信过程。IPv6实验最有意思的地方在于可以抓包看到NDP协议的握手过程。在PC1上执行ping命令之前先在R1或者PC1上开一个抓包tcpdump -i eth0 icmp6 -n -e然后从PC1 ping R1的地址。你会看到这样的报文序列PC1发送Neighbor Solicitation (NS)询问2001:db8:100::1的MAC地址。报文的目标地址是ff02::1:ff00:1这是一个组播地址只有前缀中包含了2001:db8:100::1的设备才会响应。R1收到NS后回复Neighbor Advertisement (NA)带着自己的MAC地址。之后PC1发出真正的ICMPv6 Echo RequestR1回复Echo Reply。这个过程和IPv4的ARP很像但没有广播用的是组播。好处是对链路上其他无关设备无感网络更干净。如果NS报文发出后长时间没有NA回应可能的原因有目标地址不在本链路内前缀不一致、目标设备IPv6被禁用、或者网卡之间的交换机开启了IPv6组播过滤。在实验环境里最常见的是第一种也就是前缀没配对。5.3 跨网段客户端核验Windows上的配置案例实验环境里如果混入一台Windows机器核验思路又是另一回事。很多人在Linux上配好了到了Windows上就不知道怎么看IPv6配置其实Windows也提供了完善的IPv6管理工具。在Windows下查看地址netsh interface ipv6 show addresses添加地址netsh interface ipv6 add address 以太网 2001:db8:100::20/64添加默认网关netsh interface ipv6 add route ::/0 以太网 2001:db8:100::1查看邻居表netsh interface ipv6 show neighborsWindows的ping命令直接用ping -6指定IPv6即可。5.4 排错链路地址和路由之外的两个常见坑实际排错时除了地址和路由还有两个坑非常隐蔽值得单独提醒。第一个是防火墙。无论Linux还是WindowsIPv6都有自己的防火墙链。Linux的iptables/nftables对IPv6是分开处理的如果只开了IPv4的放行规则IPv6的报文会被默认策略拦掉。检查方法很简单在目标主机上执行ip6tables -L -n或者直接临时清空规则测试ip6tables -F实验做完后记得恢复规则。第二个是重复地址检测DAD。IPv6地址配置好后不会立刻生效而是会先进入tentative状态向链路上发送NS报文看看是否有其他设备在用这个地址。如果检测到冲突地址会变成dadfailed不会被使用。在配置静态地址时如果地址和另一台设备的自动生成地址冲突了就会出现“地址明明配了但就是不生效”的情况。排查DAD状态ip -6 addr show dev eth0如果看到dadfailed换一个接口标识符或者在配置时加上nodad参数跳过检测实验环境可以这么用生产环境不建议ip -6 addr add 2001:db8:100::10/64 dev eth0 nodad6. 把实验延伸出去一套方法应对真实网络6.1 IPv6地址配置在不同场景下的侧重点三台虚拟机的实验跑通之后这套方法其实可以平移到各种真实环境只是侧重点不同家庭路由器场景运营商下发前缀路由器自动配置WAN口的IPv6地址LAN口下发的通常是SLAAC或DHCPv6-PD前缀委派。你需要关注的是路由器WAN口的获取方式和LAN口的RA配置而不是手工敲地址。企业内网场景一般会使用DHCPv6做有状态分配同时配置DNS。这时地址分配是动态的但链路本地地址仍然存在内部网络监控设备认的是全局地址还是链路本地地址取决于你要监控的目标。服务器静态配置场景对稳定性要求高的服务器地址要配成静态的并确保能通过RA定期获取默认路由和不老的DNS信息。Linux下直接用netplan或systemd-networkd配置即可。6.2 实验之后可以继续深挖的三个方向实验做完只是第一步后续有几个方向很值得继续推演IPv6地址规划一个/64前缀下有多少可用地址答案是 2 的 64 次方约1844亿亿个。但如果用SLAAC地址空间虽大路由聚合更依赖合理的前缀规划。多练习一下从上游拿到/56、/48前缀后如何划分子网。IPv6与DNSIPv6地址可读性差人的记忆根本记不住正确的DNS解析是IPv6落地的前提。重点理解AAAA记录和IPv6地址解析过程。过渡技术实际环境不可能一步到位切换到IPv6。NAT64、双栈等过渡技术的原理和配置都是实验之后的自然延伸。我在实际工作里的体会是IPv6配置的技术含量主要不在于某个命令怎么敲而在于你是否理解地址的生成逻辑、路由的转发规则、邻居发现的通讯过程。这三点贯穿了上面的所有实验步骤。只要实验做得扎实哪怕换一套操作系统、换一个厂商设备底层逻辑是不变的配置思路可以快速迁移过去。很多东西看起来神秘只是因为还没有亲手把链路跑通过一次。这次实验跑完以后再看到fe80::开头的地址你应该就能立刻反应出来这只是链路本地地址想要全局访问还得看有没有配置全局单播地址和正确的路由。
返回列表