linux 协议栈 位置,#Linux协议栈你学得会# 之 本机地址 vs 127.0.0.1

记得初学网络之时就曾困惑于ping 本机地址与 ping 127.0.0.1之间的区别与联系,也试图在网上搜寻答案,但终究还是雾里看花,似是而非。

“源码之前,了无秘密”,这次就深入Linux内核协议栈,来看看这两者之间到底是什么样的关系

凭直觉首先会想到这一定和路由表的查找有关,更进一步来说是和路由类型及外出接口有关。在Linux系统中,与外界通信相关的路由条目存储在主路由表(main)中,除此之外还有一张local表,在测试机上这两张表中的内容如下:

$ ip route

default via 192.168.28.1 dev eth0  proto static

169.254.0.0/16 dev eth0  scope link  metric 1000

192.168.28.0/24 dev eth0  proto kernel  scope link  src 192.168.28.67  metric 1

$ ip route show table local

broadcast 127.0.0.0 dev lo  proto kernel  scope link  src 127.0.0.1

local 127.0.0.0/8 dev lo  proto kernel  scope host  src 127.0.0.1

local 127.0.0.1 dev lo  proto kernel  scope host  src 127.0.0.1

broadcast 127.255.255.255 dev lo  proto kernel  scope link  src 127.0.0.1

broadcast 192.168.28.0 dev eth0  proto kernel  scope link  src 192.168.28.67

local 192.168.28.67 dev eth0  proto kernel  scope host  src 192.168.28.67

broadcast 192.168.28.255 dev eth0  proto kernel  scope link  src 192.168.28.67

可以看到,本机地址192.168.28.67和127.0.0.1对应的路由条目都存在于local表中,对应的类型为RTN_LOCAL,但相应的外出接口则不同,分别为eth0和lo。

在__ip_route_output_key函数(Kernel 3.13)中有如下的代码:

/*

* Major route resolver routine.

*/

struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)

{

...

if (res.type == RTN_LOCAL) {

if (!fl4->saddr) {

if (res.fi->fib_prefsrc)

fl4->saddr = res.fi->fib_prefsrc;

else

fl4->saddr = fl4->daddr;

}

dev_out = net->loopback_dev;

fl4->flowi4_oif = dev_out->ifindex;

flags |= RTCF_LOCAL;

goto make_route;

}

...

}

可见,当路由类型为RTN_LOCAL时,dev_out会被设置为loopback_dev,从而使用loopback设备的传输函数:

static netdev_tx_t loopback_xmit(struct sk_buff *skb,

struct net_device *dev)

{

struct pcpu_lstats *lb_stats;

int len;

skb_orphan(skb);

/* Before queueing this packet to netif_rx(),

* make sure dst is refcounted.

*/

skb_dst_force(skb);

skb->protocol = eth_type_trans(skb, dev);

/* it's OK to use per_cpu_ptr() because BHs are off */

lb_stats = this_cpu_ptr(dev->lstats);

len = skb->len;

if (likely(netif_rx(skb) == NET_RX_SUCCESS)) {

u64_stats_update_begin(&lb_stats->syncp);

lb_stats->bytes += len;

lb_stats->packets++;

u64_stats_update_end(&lb_stats->syncp);

}

return NETDEV_TX_OK;

}

loopback环回设备的输出函数最终是调用了netif_rx(),又将数据包送入协议栈的接收处理流程中,从而实现了“环回“的功能。

至此,已经从源代码的级别分析了ping本机地址和127.0.0.1的实现,实际数据都是通过loopback设备流转的。

延伸分析: 有网友分析ping本机地址与127.0.0.1的不同之处时指出当网线断开时127.0.0.1仍然是通的,但ping本机地址失败:

$ ping 192.168.28.67

connect: Network is unreachable

strace看一下是由于connect函数返回网络不可达导致的:

$ strace ping 192.168.28.67

...

...

socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = -1 EPERM (Operation not permitted)

getuid()                                = 1001

setuid(1001)                            = 0

socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3

connect(3, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("192.168.28.67")}, 16) = -1 ENETUNREACH (Network is unreachable)

dup(2)                                  = 4

fcntl(4, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)

brk(0)                                  = 0x1f4e000

brk(0x1f6f000)                          = 0x1f6f000

fstat(4, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0

mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcd94dda000

lseek(4, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)

write(4, "connect: Network is unreachable\n", 32connect: Network is unreachable

) = 32

close(4)                                = 0

munmap(0x7fcd94dda000, 4096)            = 0

exit_group(2)                           = ?

其主要原因还是在于路由表查询:

$ ip route show table local

broadcast 127.0.0.0 dev lo  proto kernel  scope link  src 127.0.0.1

local 127.0.0.0/8 dev lo  proto kernel  scope host  src 127.0.0.1

local 127.0.0.1 dev lo  proto kernel  scope host  src 127.0.0.1

broadcast 127.255.255.255 dev lo  proto kernel  scope link  src 127.0.0.1

当断开网线时,接口上的ip地址和相关的路由条目都被移除,而127.0.0.1则不受影响(只要协议栈本身正常)。

一切都很明了了。

PS. 细心的你可能会发现,在strace的结果中其实还有一个EPERM的错误,但在网络正常的情况下ping程序是可以正常运行的,那么strace中显示的这个错误是什么原因产生的呢,下一篇文章中将会专门介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值