闲谈IPv6-Anycast以及在Linux/Win7系统上的Anycast配置[通俗易懂]

闲谈IPv6-Anycast以及在Linux/Win7系统上的Anycast配置[通俗易懂]正则安安每晚每隔三小时必然哭闹,我索性也就不睡了,反正也睡不好,起来泡茶,喝酒,作文。浙江温州皮鞋

大家好,又见面了,我是你们的朋友全栈君。

正则安安每晚每隔三小时必然哭闹,我索性也就不睡了,反正也睡不好,起来泡茶,喝酒,作文。

浙江温州皮鞋?湿,下雨☔️进水不会胖!

杭州,外面依然是寒雨夜,屋里也没开空调,我穿个夏天的短袖,旁边放一杯热茶,喝完了还有昨晚实在喝不下去的汉拿山烧酒,再喝完还有上周菜市场买的米酒…作此文一篇。


在我们check IPv6的基本特征列表时,总是可以看到IPv6对Anycast的支持。说实话,对于很多人而言,这是个比较陌生的概念,对于希望看看Anycast到底是什么样子的人而言,甚至在网上很难搜到关于 如何配置Anycast 的资源。这是比较令人遗憾的。

抛开概念,那么本文尝试从不同的角度来针对Anycast探究一番。


IPv4年代的Anycast

说起Anycast,并不是在IPv6标准中突然出现的概念,一个概念怎么可能突然出现?不可能的。

早在很久很久以前,业界就针对IPv4提出了Anycast的说法,只不过相对而言,IPv6在操作上将其标准化了而已,如果说IPv4年代的Anycast标准只是 建议 ,那么IPv6的Anycast就是规定了些许 MUSTMAY

建议阅读:
RFC1546-Host Anycasting Servicehttps://tools.ietf.org/html/rfc1546
RFC3513:IPv6 Addressing Architecturehttps://tools.ietf.org/html/rfc3513#section-2.6

那么,到底如何理解Anycast?


本质上, Anycast就是将同一个IP地址配置在不同的主机网卡上,然后利用各种选路机制欺骗源主机的一种通信方式。

什么?同一个IP地址配置在不同的主机上,这不是地址冲突了吗?我们记得在初学网络基础的时候,教程上就讲过 IP地址不能冲突! 现在为什么IP冲突变成了一种通信方式了呢?真是只许州官放火,不许百姓点灯啊!

其实不然,我倒是觉得Anycast是内功深到一定程度,自然而然的一个想法。我们简单地从路由说起。

我们知道,IP地址存在的目的就是为了指挥路由器选路,最终将数据包路由到目的地,那么IP地址冲突的结果是什么?

IP地址冲突不是问题,路由冲突才是!!
IP地址冲突只有导致路由器的路由冲突(be confusing)的时候才有问题。

比如路由器R上配置的下面的两条路由:

1.1.1.1 nexthop 2.2.2.2 dev e2
1.1.1.1 nexthop 3.3.3.3 dev e3

请问一个去往目的地1.1.1.1的数据包到达路由器R之后到底是从e2走呢,还是从e3走呢?这就是问题。但是同样的路由,加一个约束就不会有问题:

1.1.1.1 nexthop 2.2.2.2 dev e2 metric 100
1.1.1.1 nexthop 3.3.3.3 dev e3 metric 10

路由器会毫不犹豫地将去往1.1.1.1的数据包从e3发出!

对于上述的两条路由,你说是IP地址冲突吗?不!并不是。

互联网本身就是一个互相网状连通的连通图,到达同一个目的地的路径不止一条,对于路由器R而言,它只管选路,逐跳转发数据包,它并不关注1.1.1.1这个目标地址到底在哪里。

好了,现在我们知道只要路由不冲突,就没有问题,数据包总是可以特定的路径,逐跳被转发,最终到达目的地。现在,我们看一下这条路是如何形成的,或者说路由器R是怎么知道到达1.1.1.1有两条路可走的。

由于这只是一篇散文,并不是技术文档,所以我并不想引入BGP,AS这些概念,姑且把全球的互联网看作是一张如下图所示的连通图:
在这里插入图片描述
然后在这张图标识的网络开始工作时,各个节点开始彼此交换自己携带的子网信息,我们可以将其理解为 路由通告。显然路由器节点P向上游路由器R0通告了子网1.1.1.0/24,意思就是说, “嗨,R0邻居兄弟,如果有到达1.1.1.0/24的包,请交给我” ,同样的信息,路由器P也会告诉它的所有其它邻居,然后路由器R0回复路由器P,放心吧,我知道了,我已经配置上了1.1.1.0/24 nexthop 4.4.4.4 dev e0,并且我也已经将这个消息转给了我的所有邻居,放心吧,它们如果有到达你那里1.1.1.0/24的包,会先交给我的…

就这样,子网信息,路由信息在整张网上彼此交换,传播,最终在每一台路由器上形成了稳定的路由表:
在这里插入图片描述
请注意,A会在两个接口同时收到关于1.1.1.0/24的通告,这并不会造成路由冲突:

  • 常规路由协议以及SPF算法均有避环的措施;
  • 有意为之的环被视为备份路由,由Metric做优先级取舍或者由ECMP管理。

如果说在这张网中有A和C需要访问1.1.1.1,那么很显然,路径如下:
在这里插入图片描述
非常好,道路是通达的,但是并不完美!Why?

这会造成路由器R1和路由器P之间的链路异常拥堵,为什么R0不能分担一部分流量呢?

嗯,我能想到的,TCP/IP标准化协会的那帮人难道能想不到?这就是ECMP的由来。再后来,干脆来个SDN全局统一分发好了…但是这和Anycast没有半毛钱的关系,所以就此打住,我们来看看另外一种引出Anycast的解法。

试想, 如果B节点也通告1.1.1.0/24的路由会怎样?

按照前面的路子,我们知道,B和P均通告相同的路由,这会造成图上所有路由器均会收到来自B和P关于1.1.1.0/24的通告,按照上面的两个基本原则:

  • 常规路由协议以及SPF算法均有避环的措施;
  • 有意为之的环被视为备份路由,由Metric做优先级取舍或者由ECMP管理。

你猜怎么着?最终成了下面的样子了:
在这里插入图片描述

是不是很像在世界互联网上部署了一个天然的负载均衡设施啊!是的!这就是所有的 IP地址冲突导致的结果! 这就是 Anycast

其精髓就是: 在路由器看来,它们并不知道不同指向的下一跳最终将数据包导向不同的目的地,它们只是认为这只是通往同一个目的地的不同路径罢了! 简单点说, Anycast之所以得以部署和实现,就是利用了IP协议逐跳寻址的特性!

事实上,Anycast的结果是,相同的IP地址位于不同的主机,因此,它的弊端也是显而易见的。

由于 逐跳的路由收敛端到端的五元组连接 之间并没有同步,因此Anycast并不适合基于端到端连接的TCP应用。

TCP并没有广域范围的连接迁移机制,因此如果路由重新收敛,将会导致连接断开!比如上述的例子,如果A到B之间的线路拥塞或者说断开,那么路由会重新收敛到A-R-R1-P这条路径,A和B的1.1.1.0/24子网的主机之间的TCP连接将会断开,这并不是人们所期望的。

因此,上述描述中的Anycast只适合于一来一回两个包的oneshot式的交互通信,比如DNS!

是的,DNS就是这么部署的,比如我们经常使用的Google DNS 8.8.8.8,它实际上就是Anycast部署在世界不同地方的多台主机,地址全部都是8.8.8.8!

请使劲阅读:https://developers.google.com/speed/public-dns/faq

然后去深撸这个站点,溯源:https://bgp.he.net
在这里插入图片描述

至此,我觉得关于Anycast的概念,已经大致陈述清楚了。

下面是IPv6的世界。我们跟踪问题本身以及其解法,跟着RFC来梳理IPv6 Anycast的来龙去脉,其实一切都很清晰。


IPv6的Anycast

重看Anycast在IPv4上的问题,我们知道, 把同一个IP地址配置在不同的主机上,这确实是不妥的,比如占据互联网流量头把交椅的TCP应用就不适合, 既然无法让主子心安理得的承认,那索性在Anycast标准化中就不要这么做就是了。

但是,只要Anycast不是部署在端节点,而是部署在路径节点,比如路由器上,那就是妥妥的。逐跳寻址原则最终导致Anycast部署在路由器上之后,会自然而然地实现ECMP,即多条路径分担同样的端到端通信。

在继续下去之前,这里先说一个观点。那就是 端到端通信多路分担这种机制对TCP是不好的!

又TMD的是TCP,是的,这里,我觉得这不是逐跳寻址的问题,这根本就是TCP的缺陷!缺陷!缺陷!

TCP要求在协议层面而不是应用层面按序到达,这就要求所有的字节最好是一路上顺序地排队前进,而多路径会影响顺序同步性,导致乱序。TCP的字节按序到达这个约束会恶化流量高峰期的链路拥塞。

由此而得到的另一个Trick就是Flowlet!它大大增加了端到端拥塞控制的复杂度。
然而我们看看QUIC,它就并没有要求严格的字节按序到达,而是基于窗口的按序到达,这就使得QUIC可以大大利用多路径分担带来的收益。

其实对于TCP而言,不光是链路最好不要多路径,甚至在路由器,交换机这种中间节点,多CPU,多处理卡也不能负载均衡分担同一条TCP流的不同数据包!

有了TCP,几乎所有的负载均衡都得按照流的粒度进行。
TCP真是太恶心了!

好了,我们先不管TCP了,任它腐烂吧!


IPv6对Anycast进行了标准化,首先在RFC3513中,它对Anycast提出了两点约束:

o An anycast address must not be used as the source address of an
IPv6 packet.

o An anycast address must not be assigned to an IPv6 host, that is,
it may be assigned to an IPv6 router only.

有了这两点约束,我们可以知道: 在IPv6中,Anycast不是用来通信的,而是用来寻址的。

紧接着,RFC3513要求 所有的路由器的所有接口 都必须配置一个 必选的Anycast地址:

2.6.1 Required Anycast Address

The Subnet-Router anycast address is predefined. Its format is as
follows:

| n bits | 128-n bits |
±———————————————–±—————+
| subnet prefix | 00000000000000 |
±———————————————–±—————+

The “subnet prefix” in an anycast address is the prefix which
identifies a specific link. This anycast address is syntactically
the same as a unicast address for an interface on the link with the
interface identifier set to zero.

Packets sent to the Subnet-Router anycast address will be delivered
to one router on the subnet. All routers are required to support the
Subnet-Router anycast addresses for the subnets to which they have
interfaces.

比方说,路由R有两个接口,分别配置了两个IP地址:

e0:  240e:909:2001::4e3/64
e1:  240e:101:4004::111/64

那么根据RFC的要求,这个路由器上将会生成下面的Anycast地址:

e0 Anycast:  240e:909:2001::/64
e1 Anycast:  240e:101:4004::/64

为了使得这些Anycast能被访问到,需要添加两条本地主机路由:

Local 240e:909:2001::/128 dev loopback
Local 240e:101:4004::/128 dev loopback

这里需要解释一点,刚才不是说IPv6的Anycast不是用来通信的吗?那 为什么还要被访问呢?

因为需要邻居解析(IPv6 Ndp)。如果有谁把这个地址设置为下一跳了,那么需要解析这个地址,这就是 被访问!

纳尼?Anycast作为下一跳?

是的,这就是IPv6 Anycast的核心用法,它不是用来标识主机服务让你的应用程序通信,它是用来寻址的:

  • Anycast地址被设置为下一跳
  • Anycast地址被设置进IPv6路由扩展头以支持源路由

来吧,我们还是举例子的好。

我的局域网为了备份,希望部署两台或者多台路由器做热备,姑且就先两台吧:
在这里插入图片描述

假设两台路由器都能接外网,如果跑IPv4协议,那么自然而然的想法就是安装keepalived跑VRRP,这种成熟的东西,想必都可以瞬间完成配置。

但是,我们知道,这两台路由器只有一台是工作状态,另外一台处于backup standby,是不是感觉浪费了资源?如果你想让它们一起工作,那就要:

  1. 为它们配置不同的IP地址,gw1,gw2;
  2. 内部局域网一半一半分别配置两个不同的gateway,即gw1,gw2。

万万不能配置成相同的IPv4地址的,因为这种地址冲突会导致交换机的转发表以及ARP表的混乱。当然,对于我个人而言,我是有办法将其配置成相同的IP地址又不会confuse各种表的,但是,配置太复杂了(涉及iptables,ebtables,arptables,arp,iproute2,STP等等)。正如IPv4的Anycast一样,没有什么是IPv4配置不出来的,只是这些大部分都是奇技淫巧般的Trick!玩物丧志!

我们看一下用IPv6会怎样。

不用干别的,如果路由器的实现遵循RFC标准,那么你只需要配置两台路由器不同的同网段地址即可,如下:

路由器1 e0: 240e:110:1001::fbb2:0a1e:0e59:15eb/64  [低64bit为EUI-64映射而来]
路由器2 e0: 240e:110:1001::eb7c:b2da:7088:3c38/64  [低64bit为EUI-64映射而来]

然后所有主机的默认网关设置成其Anycast地址即可:

::/0 nexthop 240e:110:1001::

就这么简单!Why?

因为路由器1和路由器2在配置好内网e0的地址后,根据RFC3513,它们会自动生成Anycast地址以及Anycast地址的主机路由以被内网主机邻居解析。

RFC要求各层寻址Anycast地址时以 本层次的路由距离 来度量,那么对于二层链路,其距离默认就是物理距离,光速不变,距离等效于时间,因此会等效为 谁先回复我的邻居请求,谁就是我要寻址的Anycast节点!
在这里插入图片描述
是不是天然的负载均衡了呢?炫酷吧!

现在有个不可回避的问题需要解决,那就是 访问互联网服务的正向包和返回包路径不对称问题。

比如,h1主机访问服务器S走的R1作为默认网关,然而S返回h1时,却从R2返回,此时R2解析h1的地址,h1收到R2的解析请求后,会不会更新自己关于Anycast邻居的信息呢?如果是的话,那势必会造成邻居表信息的剧烈抖动啊!

答案是 并不会! Why?

因为IPv6在解析邻居时,ICMPv6协议头里会写清楚下面的信息:

  • 自己是不是路由器
  • 邻居信息需不需要覆盖

这一点和IPv4的ARP不同,ARP是双向更新的,在回复自己的MAC地址时,同时也更新了自己的ARP表,但在IPv6中,两者分开了:

  • 请求你的MAC信息
  • 请更新你自己的邻居信息

R2发送给h1的邻居请求,只是请求h1的MAC地址而已,并没有说要h1更新其邻居信息,所以万事大吉:
在这里插入图片描述


配置和实现

现在该看看实现了。

很少有资料讲 如何在Linux上配置IPv6的Anycast 的,这一次可能我又占了坑。

其实很简单,只要开启IPv6的转发即可:

sysctl -w net.ipv6.conf.all.forwarding=1

这个时候,你就会在/proc/net/anycast6看到内容:

[root@localhost src]# cat /proc/net/anycast6
3    enp0s8          fe800000000000000000000000000000     1
4    enp0s9          fe800000000000000000000000000000     1
4    enp0s9          240e0918800300000000000000000000     1

我在enp0s9上配置了如下的IPv6地址:

[root@localhost src]# ip -6 addr ls dev enp0s9
4: enp0s9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qlen 1000
    inet6 240e:918:8003::3f5/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::eb7c:b2da:7088:3c38/64 scope link noprefixroute
       valid_lft forever preferred_lft forever

所以说,什么都不用干,Linux内核自动帮我生成了其对应的Anycast地址,对应RFC3513的2.6.1 Required Anycast Address格式:240e:918:8003::

按照上面一个小节最后的例子,我们知道,这个 240e:918:8003:: 是可以被邻居发现而解析的,而我们知道,IPv6的邻居发现使用的是组播地址,其组播构成规则详见:
RFC3513-2.7 Multicast Addresseshttps://tools.ietf.org/html/rfc3513#section-2.7

对应组播地址:
FF02::1:FF00:0000/104 Solicited-Node Address [RFC3513] -RFC4291对其进行了增强更新

Solicited-Node Address: FF02:0:0:0:0:1:FFXX:XXXX

Solicited-node multicast address are computed as a function of a
node’s unicast and anycast addresses. A solicited-node multicast
address is formed by taking the low-order 24 bits of an address
(unicast or anycast) and appending those bits to the prefix
FF02:0:0:0:0:1:FF00::/104 resulting in a multicast address in the
range

FF02:0:0:0:0:1:FF00:0000
to
FF02:0:0:0:0:1:FFFF:FFFF

我们针对Linux的如上配置确认一下:

[root@localhost src]# cat /proc/net/igmp6
1    lo              ff020000000000000000000000000001     1 0000000C 0
1    lo              ff010000000000000000000000000001     1 00000008 0
...
# 下面这个便是!
4    enp0s9          ff0200000000000000000001ff000000     2 00000004 0
...

将Anycast地址作为默认网关发送数据,最终邻居解析的时候,只要发送到组播地址 ff02::1:FF00:: 就可以解析出该网段上的Anycast地址的MAC地址信息,然后 取第一个到达的作为邻居 即可!

上面关于组播的设置,请看 addrconf_join_anycast 函数:

static void addrconf_join_anycast(struct inet6_ifaddr *ifp)
{ 
   
    struct in6_addr addr;

    if (ifp->prefix_len >= 127) /* RFC 6164 */
        return;
    ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
    if (ipv6_addr_any(&addr))
        return;
    ipv6_dev_ac_inc(ifp->idev->dev, &addr);
}

其中,ipv6_dev_ac_inc 值得观摩!


配置也配好了,那么我们找两台机器练一练手吧。

这次我部署的另外一个机器是Windows 7系统,顺便玩一下netsh。这台Win7系统机器和我们的Linux Rh7.2直连,拓扑我就不画了,非常简单。我只是把Win7上的地址配置发布出来。

很简单,Win7上配置一个 240e:918:8003::/64* 同网段的IPv6地址:
在这里插入图片描述

这个时候,将Win7的默认网关设置成 240e:918:8003::* 这个Linux上使能的Anycast地址,看看如何通信。

按照惯例,ping一下这个 240e:918:8003::* 地址:
在这里插入图片描述
不通!

在Linux上抓包,发现是有回复ICMPv6 Echo Reply的,只是说回复的源IP地址不是Win7期望的Anycast地址,而是Linux上enp0s9网卡的地址,这正是印证了 An anycast address must not be used as the source address of an IPv6 packet. 这句话!

我很好奇Linux内核是怎么做到 不让Anycast地址作为源地址的 ,ping不行,TCP的Telnet也不行…其实看一下代码就完全明白了。

先看下Telnet为什么完全就没有SYN-ACK回复:
在这里插入图片描述
再看看为什么ping回复的时候用的不是Anycast地址,而是选择了网卡上配置的地址:
在这里插入图片描述
这个关于源地址选择的细节,详见RFC3484以及我的前一篇闲谈:
Default Address Selection for Internet Protocol version 6 (IPv6)https://www.ietf.org/rfc/rfc3484.txt
闲谈IPv6-源IP地址的选择(RFC3484读后感)https://blog.csdn.net/dog250/article/details/87815123


脉络理清了之后,我们来反一下,让Win7主机配置Anycast。

我特意没有按照RFC的规定,去配置了一个非标准的Required Anycast Address,且看:
在这里插入图片描述
配置方法如下:
在这里插入图片描述

我并没有让低n-bit为全0,竟然成功了,这说明Win7并没有严格按照RFC的规范行事,它完全是手动的。

那么好,我在Linux上去ping这个Win7的Anycast地址:
在这里插入图片描述

得到了Win7的回复,然而源地址不是Win7的Anycast地址,却是Win7的物理网卡 本地连接 3 上配置的IPv6地址。这依然印证了 An anycast address must not be used as the source address of an IPv6 packet. 这句话。

只是说,Win7对Anycast地址 并没有严格遵循subnet后面的低bits均为0的约束 ,即Win7没有实现 严格的Required Anycast Address!

不管怎么样,痛则不通,通则不痛,后面也没啥可玩的了。


总结

IPv6的Anycast:

  • 以往IPv4的规则在IPv6 RFC约束下依然适用
  • 独添了subnet-Anycast,见上文

因为我们可以将Anycast总结为:

  1. 广义的Global Anycast-作用于IPv4/IPv6
  2. 狭义的IPv6 subnet Anycast-作用于IPv6

此外,RFC2526又规定了 保留的Anycast地址 用于不同的目的:
RFC2526-Reserved IPv6 Subnet Anycast Addresseshttps://tools.ietf.org/html/rfc2526
本文不涉及RFC2526的内容,但是提醒注意,仅此而已。

阅读笔记

为了写这篇文章,我深夜快速浏览了RFC标准,因为我要在这些个标准之上才能胡扯形而上吧。

这两个截图体现了IPv6 Anycast的作用,它到底用在哪?我想我在上文呢,已经阐述清楚了。
在这里插入图片描述

在这里插入图片描述


后记

为Anycast添加一条host路由,让anycast地址可以被设置成网关。

昨晚睡的有点早,这周末又要去公司附近拉一车行李,有点忙。白天基本没空,所以就以安安吃奶闹铃为由,作文一篇。

浙江温州皮鞋湿,下雨进水不会胖。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/142198.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 【跃迁之路】【502天】刻意练习系列261(2018.06.22)

    【跃迁之路】【502天】刻意练习系列261(2018.06.22)

  • 基于经纬度做航线图可视化的软件_碧蓝航线是哪家公司做的

    基于经纬度做航线图可视化的软件_碧蓝航线是哪家公司做的基于经纬度画航线图介绍代码介绍这阵子在处理航空公司的数据,为了PPT展示好看,做了几个可视化图。这里用的是pyecharts第三方库。pyecharts库的相关介绍,可以上设计文档看看相关说明。https://pyecharts.org/#/zh-cn/series_options代码importpandasaspddata=pd.read_csv(“airline_info.csv”,encoding=’gbk’)print(data)#数据太多,画出来太密了,这里选了

  • ORACLE EXP命令

    ORACLE EXP命令

  • java 日志处理[通俗易懂]

    java各日志组件介绍common-logging(同时也称JCL)  common-logging是apache提供的一个通用的日志接口。用户可以自由选择第三方的日志组件作为具体实现,像log4j,或者jdk自带的logging,common-logging会通过动态查找的机制,在程序运行时自动找出真正使用的日志库。当然,common-loggi…

  • 软件管理和电脑管家打不开怎么办_电脑管家下载软件连接错误

    软件管理和电脑管家打不开怎么办_电脑管家下载软件连接错误错误:应用程序无法启动,因为应用程序的并行配置不正确。请参阅应用程序事件日志,或使用命令行sxstrace.exe工具”问题的处理方法。方法一:开始-运行(输入services.msc)-确定或回车,打开:服务(本地);我们在服务(本地)窗口找到:WindowsModulesInstaller服务,查看是否被禁用;3…如果WindowsModulesInstaller服务被禁用,我们必须把它更改为启用-手动,重启计算机,再安装应用程序。转载至https://blo

  • Calendar类_java calendar

    Calendar类_java calendarCalendar类简介Calendar是javautil包下的一个工具类,提供了很方便的不同日期格式的处理。啥也不说了,直接撸代码:publicstaticvoidmain(String[]args){System.out.println(“————Calendar无参构造————“);//Cal…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号