路由存在的情况下ping提示Destination Host Unreachable
问题现象:ping xxxx 提示
# ping 121.10.41.62
PING 121.10.41.62 (121.10.41.62) 56(84) bytes of data.
From 14.116.225.5: icmp_seq=2 Destination Host Unreachable
首先要明白 Destination Host Unreachable 代表了什么??
说明了:"没有跟对方建立连接";此链接并非tcp的链接
那怎么排查呢?
1、首先查看路由是否正确有没有;通过netstat -rn route -nv ip route 等命令查看 ping 41.62 地址时 需要的路由是否正确
根据路由结果 此时报文应该走默认路由 出去 ;
查看ip neigh arp -nv 默认路由邻居表项存在
2、路由route 以及neigh 都存在 为什么还是出现这个错误呢?
那就只能从头到位排查了:
- 首先ping lo 查看tcp/ip 协议栈是否正确
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.033 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
- ping ethx 接口ip 同一个网段的地址;也能通 说明此时接口设备驱动没问题,报文能发送出去也能收包
Tcp/ip 协议栈 dev 设备驱动都是正常 路由 neighbour 也是正常----怎么还是有问题??
难道是查看的东西 使用的工具有问题?
在回头想一下: ping lo 证明了协议栈没问题 ping dev 说明驱动基本ok;
route -nv 说明有了路由---------有了路由 报文就会从这个路由走吗??
万一是从别的路由走呢???
所以通过ip route get 121.10.41.62 发现其路由 via 是个莫名其妙的地址
同时查看ip neigh 发现有这个莫名其妙的地址对应的 表项;不过此表项为failed;
所以此时就能解释通了:ping 此ip 地址提示Destination Host Unreachable。
报文查找路由的时候出现错误,找到错误的出口路由ip;此时路由出口IP的arp 表项不存在
所以导致了 上述错误!!
那么后续需要处理的问题时---为什么出现上述错误:路由查找错误?
$ip route get 121.10.41.62 121.10.41.62 via 10.16.143.108 dev eth8 src 14.116.225.5 cache ipid 0x0bca rtt 186ms rttvar 144ms ssthresh 51 cwnd 46 reordering 9
通过分析 rt_cache 相关信息格局rt_flag 其值带有 RTCF_REDIRECTED 标志,表示其路由被重定向了;
这个可以根据cat /proc/net/rt_cache 输出;
源码分析步骤为:在代码中搜索 rt_gateway
发现在创建路由的时候回赋值, 不可能,一开始是好的, 再就是check_peer_redir 以及 rrt_init_metrincs rt_set_netxhop的时候会出现;
仔细分析代码 目前认为check_peer_redir 会修改路由gw;
目前发下 ipv4_dst_check
----->check_peer_redir 来检查路由是否被redir
那么ipv4_dst_check 有没有可能会被调用到呢?
static struct dst_ops ipv4_dst_ops = { .family = AF_INET, .protocol = cpu_to_be16(ETH_P_IP), .gc = rt_garbage_collect, .check = ipv4_dst_check, .default_advmss = ipv4_default_advmss, .default_mtu = ipv4_default_mtu, .cow_metrics = ipv4_cow_metrics, .destroy = ipv4_dst_destroy, .ifdown = ipv4_dst_ifdown, .negative_advice = ipv4_negative_advice, .link_failure = ipv4_link_failure, .update_pmtu = ip_rt_update_pmtu, .local_out = __ip_local_out, }; static struct rtable *rt_dst_alloc(bool nopolicy, bool noxfrm) { struct rtable *rt = dst_alloc(&ipv4_dst_ops, 1); if (rt) { rt->dst.obsolete = -1; rt->dst.flags = DST_HOST | (nopolicy ? DST_NOPOLICY : 0) | (noxfrm ? DST_NOXFRM : 0); } return rt; }
也就是创建路由表项的时候对路由设置ops;所以就直接看check函数什么时候调用了!!
对于tcp 协议发包来说,根据以前的文章可知:调用路由先关检查只有可能在ip_queue_xmit之后,走读ip_queue_xmit相关代码;
skb->dev = dev; skb->protocol = htons(ETH_P_IP); return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev, ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } int ip_queue_xmit(struct sk_buff *skb) { struct sock *sk = skb->sk; ---------------------------------------- rcu_read_lock(); rt = skb_rtable(skb); if (rt != NULL) goto packet_routed; /* Make sure we can route this packet. */ rt = (struct rtable *)__sk_dst_check(sk, 0); ----------------------------- }
struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie) { struct dst_entry *dst = __sk_dst_get(sk); if (dst && dst->obsolete && dst->ops->check(dst, cookie) == NULL) { sk_tx_queue_clear(sk); rcu_assign_pointer(sk->sk_dst_cache, NULL); dst_release(dst); return NULL; } return dst; }
可知 在IP层会检查路由 是否正确的问题也就是 可能存在rtdir导致路由切换
这里面有个比较好的工具: ip route get xxx 可以直接查找其对应的路由
相关命令有:
- ip route show cache
- ip route flush cache
- ip route list/show
- ip neigh
- ip addr
- ip link
ip 命令和ifconfig route arp 等命令相似并且包含它们的功能---可以好好研究一下
现在来详细看下为什么会提示Destination Host Unreachable;
协议栈封包时 ;ip层报文处理完后,需要填充对应的mac 地址; 所以需要根据目的出口Ip来填充mac,由于出口路由via 是 10.16.143.108 ;
1、发出arp req报文请求此ip 地址对应的mac 地址;
2、发出arp 请求报文后,等待arp 应答报文,如果超时 就会调用
neigh_timer_handler---->neigh_invalidate--->
neigh->ops->error_report(arp_error_report)
---->dst->ops->link_failure(skb);---->ipv4_link_failure
---->kfree_skb
[752934.081279] [<ffffffff8151cd99>] ipv4_link_failure+0x1d/0x70 [752934.081281] [<ffffffff8154436b>] arp_error_report+0x30/0x3c [752934.081284] [<ffffffff814dc741>] neigh_invalidate+0x4b/0x7e [752934.081286] [<ffffffff814dd2b0>] neigh_timer_handler+0x181/0x258 [752934.081289] [<ffffffff81054497>] run_timer_softirq+0x16e/0x200 [752934.081292] [<ffffffff814dd12f>] ? neigh_update+0x3c4/0x3c4
这里主要看下:ipv4_link_failure
static void ipv4_link_failure(struct sk_buff *skb) { struct rtable *rt; icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); rt = skb_rtable(skb); if (rt) dst_set_expires(&rt->dst, 0); }
这也就是: 通过发送icmp报文提示 dest/host unreach
此时 源地址就是arp 发送使用的source ip
ping 10.67.10.173 PING 10.67.10.173 (10.67.10.173) 56(84) bytes of data. From 10.67.10.174 icmp_seq=1 Destination Host Unreachable From 10.67.10.174 icmp_seq=5 Destination Host Unreachable From 10.67.10.174 icmp_seq=6 Destination Host Unreachable From 10.67.10.174 icmp_seq=7 Destination Host Unreachable From 10.67.10.174 icmp_seq=11 Destination Host Unreachable From 10.67.10.174 icmp_seq=12 Destination Host Unreachable From 10.67.10.174 icmp_seq=13 Destination Host Unreachable
tcpdump -i lo icmp -ne tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes 06:40:42.484220 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 126: 10.67.10.174 > 10.67.10.174: ICMP host 10.67.10.173 unreachable, length 92 06:40:45.484218 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 126: 10.67.10.174 > 10.67.10.174: ICMP host 10.67.10.173 unreachable, length 92 06:40:45.484226 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 126: 10.67.10.174 > 10.67.10.174: ICMP host 10.67.10.173 unreachable, length 92 06:40:48.484220 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 126: 10.67.10.174 > 10.67.10.174: ICMP host 10.67.10.173 unreachable, length 92 06:40:51.484219 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 126: 10.67.10.174 > 10.67.10.174: ICMP host 10.67.10.173 unreachable, length 92 06:40:51.484225 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 126: 10.67.10.174 > 10.67.10.174: ICMP host 10.67.10.173 unreachable, length 92
可以看到 eth1 接口ip 为10.67.10.174 时 去ping 一个不存在主机的ip 10.67.10.173 ;
在lo 接口上抓包;可以看到对应的icmp 报文;
报文如下:
也就是 icmp报文中 的控制信息中会带上触发这个icmp报文的原始报文
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战