前些日子在IDC实验docker的时候,为了不与公司网络冲突,将bridge设置为127.x网段的IP,原觉得这样就OK,后来发如今访问container内部的服务的时候没法访问。开始觉得iptables的问题,搞了半天,后来,才发现系统对127.x.x.x的包根本不会通过bridge。这两天补习了一下linux的路由实现,才完全明白其中原因。linux
其实,关于环回接口,TCP/IP详解中已经描述得很清楚,只是本身没有去仔细阅读而已。docker
Linu支持环回接口( Loopback Interface),以容许运行在同一台主机上的客户程序和服务器程序通TCP/IP进行通讯。 A 类网络127就是为环回接口预留的 。根据惯例,大多数系统把IP地址127.0.0.1分配给这个接口,并命名为localhost。一个传给环回接口的IP数据报不能在任何网络上出现。实际上,访问127.x.x.x的全部IP都是访问环回接口(lo)。服务器
按理来讲,一旦传输层检测到目的端地址是环回地址时,应该能够省略部分传输层和全部网络层的逻辑操做。可是大多数的产品仍是照样完成传输层和网络层的全部过程,只是当 I P 数据报离开网络层时把它返回给本身。Linux的内核实现就是这样。网络
几个关键点:oop
(1)传给环回地址(通常是127.0.0.1 )的任何数据均做为IP输入。url
(2)传给广播地址或多播地址的数据报复制一份传给环回接口,而后送到以太网上。这是由于广播传送和多播传送的定包含主机自己。spa
(3)任何传给该主机I P地址的数据均送到环回接口 。blog
从上面的描述能够明白,访问127.0.0.1和本机IP(好比192.168.1.10)都是经过lo来完成的。接口
考虑以下路由表:ip
尽管咱们为172.16.213.0/24和129.0.0.0/8指定了出口设备(eth0/docker0),但实际上,数据仍然是经过lo来完成的。
内核默认有两个路由表(不考虑策略路由):
struct fib_table *ip_fib_local_table;
struct fib_table *ip_fib_main_table;
前者用于本地路由,后均可以由管理员配置。
从上面的能够看到,172.16.213.129,127.0.0.0/8都被认为是本机IP。
linux在进行路由查找时,先查找local,再查找main:
static inline int fib_lookup(const struct flowi *flp, struct fib_result *res) { if (ip_fib_local_table->tb_lookup(ip_fib_local_table, flp, res) && ip_fib_main_table->tb_lookup(ip_fib_main_table, flp, res)) return -ENETUNREACH; return 0; } |
实际上,若是内核认为目标地址是本机IP,就会将包的出口设备设置为loopback_dev(无论路由表将出口设备设置成什么)。
static int ip_route_output_slow(struct rtable **rp, const struct flowi *oldflp) { ... if (res.type == RTN_LOCAL) { if (!fl.fl4_src) fl.fl4_src = fl.fl4_dst; if (dev_out) dev_put(dev_out); dev_out = &loopback_dev; dev_hold(dev_out); fl.oif = dev_out->ifindex; if (res.fi) fib_info_put(res.fi); res.fi = NULL; flags |= RTCF_LOCAL; goto make_route; } |
整个数据流过程:
主要参考
[1]TCP/IP详解
[2]深刻理解linux网络技术内幕
做者:YY哥
出处:http://www.cnblogs.com/hustcat/ 本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接,不然保留追究法律责任的权利。